Ett BDD-experiment, observation ett

13 maj 2008

Jag började uppifrån-och-ned, som är tanken i BDD. Då blev jag satt i följande situation:

1. Indata till NAutoDoc är en sökväg till en mapp som ska scannas.
2. Utdata från NAutoDoc är en HTML-fil.

Försökte mig på detta i kod:

public interface IFileWriter {
  void WriteFile(string path, string content);
}

[Test]
public void NoInputFilesDoesNotProduceAnOutputFile()
{
  // Dependency
  DynamicMock fileWriter = new DynamicMock(typeof(IFileWriter));
  fileWriter.ExpectNoCall("WriteFile");

  // SUT (subject under test)
  TestDocGenerator gen = new TestDockGenerator(
    (IFileWriter)fileWriter.MockInstance,
    "somepath");
  gen.GenerateReport("report.html");

  fileWriter.Verify();
}

Men här låser det sig i min hjärna: hur specificerar jag att ”somepath” inte innehåller några filer? Jag tänkte mig att TestDocGenerator skulle instansiera någon ”filescanner”-klass mha. ”somepath” internt, och använda denna för att rekursivt söka igenom en sökväg.

Satt fast med denna problematik nästan en hel dag innan jag kom på mitt tankefel: ”somepath” är ett beroende! Det borde alltså mockas, inte instansieras internt i TestDocGenerator!

Försök två blir alltså:


public interface IFileList {
 List<string> GetFiles();
 }
 public interface IFileWriter {
 void WriteFile(string path, string content);
 }

[Test]
 public void NoInputFilesDoesNotProduceAnOutputFile()
 {
 // Dependency
 DynamicMock fileList = new DynamicMock(typeof(IFileList));
 fileList.ExpectAndReturn("WriteFile", new List<string>());
 DynamicMock fileWriter = new DynamicMock(typeof(IFileWriter));
 fileWriter.ExpectNoCall("WriteFile");

// SUT (subject under test)
 TestDocGenerator gen = new TestDockGenerator(
 (IFileList)fileList.MockInstance,
 (IFileWriter)fileWriter.MockInstance);
 gen.GenerateReport("report.html");

fileList.Verify();
 fileWriter.Verify();
 }
 

Slutsats: att instansiera grejer i konstruktor eller annanstans verkar inte vara BDD-vänligt! Det är detta Michael Feathers kallar ett ”internt beroende” i sin bibel ”Working Efficiently With Legacy Code” om jag minns rätt. Det är ett hälsotecken; BDD tvingar fram renare kod.

Läs även andra bloggares åsikter om , , ,


Ett BDD-experiment: NAutoDoc

12 maj 2008

Har läst om Behaviour Driven Development, artikeln ”Mocks Aren’t Stubs” av Martin Fowler först, som gjorde skillnad på begreppen Dummy, Fake, Stub och Mock. Det var den artikeln som fick upp mitt intresse för BDD. Surfade runt ett tag och fastnade för artikeln ”A new look at test-driven development” av Dave Astels, en artikeln som verkar ligga på framkanten av BDD. Bland annat så diskuteras ramverket rSpec (Ruby Specification) som låg i sin linda när artikeln skrevs, och bygger på artikelns nya kontext-inriktade synsätt.

I alla fall så håller jag nu på att testa (no pun intended!) detta nya angreppssätt att utveckla programvara. Det lilla experimentet blir en enkel implementation av TestDoc-idéen, dvs. att testkod ofta är bra dokumentation av klasser. Det funkar så här. Säg att vi utvecklar en medelvärdes-klass. Vi bygger ett test som kontrollerar att medelvärdet av ”inga tal” inte går att beräkna, och ska ge ett Exception. Så här ser det ut med NUnit:

[TestFixture]
public class MeanComputerFixture {
  [Test, ExpectedException]
  public void CallingComputeWithNoAddsIsAnError() {
    MeanComputer cpu = new MeanComputer();
    double mean = cpu.Compute();
  }
  [Test]
  public void TheMeanOfASingleValueIsTheValueItself() {
    MeanComputer cpu = new MeanComputer();
    cpu.Add(5);
    Assert.AreEqual(5, cpu.Compute());
  }
}

Detta lilla test skulle producera följande ”dokumentation” av MeanComputer-klassen:

MeanComputer:
– calling compute with no adds is an error
– the mean of a single value is the value itself

Lite nice, och förhoppningsvis något som är användbart också i praktiken. Planerar köra det på både mitt spelprojekt (Dogfight2008) och på jobbprojektet. (nackdelen som jag ser det är de obekvämt långa metodnamnen…)

Detta känns som ett lagom stort projekt att ta sig an BDD med. Kommer att parsa .cs-filer direkt, och generera en HTML-fil som resultat. Eller varför inte en .rtf-fil..? Har varit sugen på att lära mig .rtf-filformatet då det innehåller page-breaks till skillnad från HTML, något som kan vara riktigt användbart.

Läs även andra bloggares åsikter om , , ,


%d bloggare gillar detta: