Datenpunkte

Verhaltensgesteuerter Entwurf mit SpecFlow

Julie Lerman

Julie LermanInzwischen werden Sie mit meiner Vorliebe vertraut sein, Entwickler in die von mir in Vermont geleitete Benutzergruppe einzuladen, damit sie dort über Themen sprechen, auf die ich neugierig bin. Das führte schon zu Kolumnen über solche Themen wie Knockout.js und Breeze.js. Natürlich gibt es noch weitere Themen, wie etwa CQRS (Command Query Responsibility Segregation - Zuständigkeitstrennung bei Befehlsabfragen), an denen ich schon seit einiger Zeit herumknabbere. Dann aber hat Dennis Doire, ein Architekt und Tester, vor Kurzem über SpecFlow und Selenium gesprochen. Das sind zwei Tools für Tester, die sich mit verhaltensgesteuerter Entwicklung (BDD – Behavior-Driven Development) befassen. Wieder einmal wurden meine Augen immer größer und ich begann gedanklich schon nach einer Entschuldigung zu suchen, um einmal ein wenig mit diesen Tools spielen zu können. Vor allem aber hat die verhaltensgesteuerte Entwicklung meine Aufmerksamkeit erregt. Obwohl ich durchaus ein Datenmensch bin, liegen die Tage meiner datenbankbasierten Anwendungsentwürfe schon eine Weile zurück, und ich konzentriere mich inzwischen eher auf die Domäne.

Die verhaltensgesteuerte Entwicklung ist eine besondere Form der testgesteuerten Entwicklung (TDD – Test-Driven Development), bei der die User Story im Mittelpunkt steht und die Logik sowie der Test um diesen Hintergrund herum konzipiert werden. Anstatt eine einzelne Regel zu erfüllen, wird ganzen Sätzen von Aktivitäten entsprochen. Es ist eine ausgesprochen ganzheitliche Angelegenheit, und das liebe ich. Deshalb interessiert mich diese Sichtweise gewaltig. Während mit typischen Komponententests möglicherweise sicherstellt werden kann, dass ein Ereignis bei einem Kundenobjekt korrekt funktioniert, steht hinter BDD der Gedanke, sich auf die umfassendere Story des Verhaltens zu konzentrieren, das der Benutzer bei Verwendung des für ihn erstellten Systems erwartet. Dabei wird BDD häufig bei Kundengesprächen zum Festlegen der Akzeptanzkriterien eingesetzt. Wenn ich zum Beispiel vor dem Computer sitze, das Anmeldeformular für einen Neukunden ausfülle und dann auf „Speichern“ klicke, sollten die Kundeninformationen vom System gespeichert und mir dann eine Meldung angezeigt werden, dass die Daten erfolgreich gespeichert wurden.

Oder vielleicht sollte automatisch der zuletzt von mir in meiner letzten Sitzung bearbeitete Kunde angezeigt werden, sobald ich den Kundenverwaltungsbereich einer Software aktiviere.

Sie können anhand dieser User Storys feststellen, dass BDD eine an der Benutzeroberfläche orientierte Technik zum Entwickeln und Entwerfen automatisierter Test sein könnte. Doch viele dieser Szenarien werden geschrieben, noch bevor eine Benutzeroberfläche entworfen wurde. Und dank solcher Tools wie Selenium (docs.seleniumhq.org) und WatiN (watin.org) können Tests im Browser automatisiert werden. Allerdings ist BDD nicht einfach zum Beschreiben der Benutzerinteraktion gedacht. Um das große Ganze hinter BDD besser zu verstehen, werfen Sie doch einen Blick auf die Diskussion zu InfoQ, die auf bit.ly/10jp6ve unter einigen Autoritäten aus den Bereichen BDD, TDD und der Spezifikation nach Beispiel geführt wird.

Ich möchte mir über Schaltflächenklicks und Ähnliches jetzt keine Gedanken machen und die User Storys ein wenig neu definieren. Ich kann die von der Benutzeroberfläche abhängigen Elemente der Story entfernen und mich auf den Teil des Prozesses konzentrieren, der nicht mit dem Bildschirm zusammenhängt. Und natürlich haben die Storys, an denen ich interessiert bin, mit Datenzugriff zu tun.

Es kann mühselig sein, eine Logik zu erstellen, mit der getestet wird, ob ein bestimmtes Verhalten erfüllt wird. Eins der von Doire in seiner Präsentation vorgestellten Tools war SpecFlow (specflow.org). Dieses Tool wird in Visual Studio integriert und ermöglich es Ihnen mithilfe einiger einfacher Regeln, User Storys (auch Szenarien genannt) zu definieren. Mit dem Programm wird dann die Erstellung und Ausführung der Methoden (einige mit Tests, andere ohne Tests) teilweise automatisiert. Das Ziel dabei ist, zu überprüfen, ob den Regeln der Story auch entsprochen wird.

Ich werde Sie jetzt durch den Erstellungsprozess einiger Verhaltensweisen führen, und wenn Sie danach auf den Geschmack gekommen sind, finden Sie weitere Ressourcen am Ende dieses Artikels.

Als Erstes müssen Sie SpecFlow in Visual Studio installieren. Das können Sie vom Visual Studio Update-Manager aus erledigen. Da BDD darauf ausgerichtet ist, die Entwicklung eines Projekts mit der Beschreibung von Verhaltensweisen zu beginnen, handelt es sich bei dem ersten Projekt der Lösung um ein Testprojekt, in dem diese Verhaltensweisen beschrieben werden. Der Rest der Lösung entspringt dann an diesem Punkt.

Erstellen Sie ein neues Projekt mithilfe der Projektvorlage für Komponententests. Für das Projekt ist ein Verweis zur TechTalk.SpecFlow.dll erforderlich, die mit NuGet installiert werden kann. Erstellen Sie in diesem Projekt dann einen Ordner namens „Features“.

Mein erstes Feature wird auf Grundlage einer User Story erstellt, bei der es um das Hinzufügen eines neuen Kunden geht. Also erstelle ich im Features-Ordner einen weiteren Ordner namens „Add“ (siehe Abbildung 1) Hier definiere ich das Szenario und verwende SpecFlow als Hilfestellung.

Test Project with Features and Add Sub-Folders
Abbildung 1: Testprojekt mit den Unterordnern „Features“ und „Add“

SpecFlow folgt einem bestimmten Muster auf Grundlage von Schlüsselwörtern, die bei der Beschreibung von Features hilft, deren Verhalten definiert werden. Die Schlüsselwörter stammen aus einer Sprache namens Gherkin, und all das hat seinen Ursprung in einem Tool namens Cucumber (cukes.info). Einige dieser Schlüsselwörter sind: Given, And, When und Then. Sie können zum Erstellen eines Szenarios verwendet werden. Zum Beispiel dieses einfache in einem Feature gekapselte Szenario hier – Hinzufügen eines Neukunden:

Given a user has entered information about a customer
When she completes entering more information
Then that customer should be stored in the system

Das lässt sich auch noch weiter ausarbeiten. So zum Beispiel:

Given a user has entered information about a customer
And she has provided a first name and a last name as required
When she completes entering more information
Then that customer should be stored in the system

An dieser letzen Anweisung werde ich ein wenig Data Persistence betreiben. Für die Arbeit mit SpecFlow ist es unerheblich, wie das alles zustande kommt. Am Schluss soll ein Szenario stehen, mit dem ein erfolgreiches Ergebnis belegt werden kann. Mit dem Szenario wird der Testsatz gesteuert, und die Tests helfen bei der Ausarbeitung der Domänenlogik:

Given that you have used the proper keywords
When you trigger SpecFlow
Then a set of steps will be generated for you to populate with code
And a class file will be generated that will automate the execution of these steps on your behalf

Wir untersuchen, wie das funktioniert.

Klicken Sie mit der rechten Maustaste auf den Ordner „Add“, um ein neues Element hinzuzufügen. Wenn Sie SpecFlow installiert haben, werden bei einer Suche nach „specflow“ drei auf SpecFlow-bezogene Elemente gefunden. Wählen Sie das Element „SpecFlow Feature File“ aus und geben Sie ihm einen Namen. Meins habe ich „AddCustomer.feature“ genannt.

Eine Featuredatei beginnt mit einem Beispiel; der universellen math-Funktion. Beachten Sie, dass das Feature oben beschrieben wird, und dass ein Szenario (das ein Schlüsselbeispiel des Features darstellt) mittels Given, And, When und Then unten beschrieben wird. Mit dem SpecFlow-Add-In wird sichergestellt, dass der Text farbkodiert ist, damit Sie die Ausdrücke der einzelnen Schritte Ihrer Anweisungen leichter auseinanderhalten können.

Ich ersetze das Konservenfeature und die Schritte durch meine Eigenen:

Feature: Add Customer
Allow users to create and store new customers
As long as the new customers have a first and last name

Scenario: HappyPath
Given a user has entered information about a customer
And she has provided a first name and a last name as required
When she completes entering more information
Then that customer should be stored in the system

(Vielen Dank an David Starr für den Namen des Szenarios! Ich habe mir erlaubt, ihn von seinem Pluralsight Video zu stehlen.)

Was passiert, wenn die erforderlichen Daten nicht bereitgestellt werden? Um diese Möglichkeit zu behandeln, erstelle ich ein anderes Szenario in diesem Feature:

Scenario: Missing Required Data
Given a user has entered information about a customer
And she has not provided the first name and last name
When she completes entering more information
Then that user will be notified about the missing data
And the customer will not be stored into the system

Das sollte für das Erste genügen.

Von der User Story zu etwas Code

Bisher haben Sie das Featureelement und die von SpecFlow bereitgestellte Farbkodierung zu sehen bekommen. Beachten Sie, dass der Featuredatei eine Code-Behind-Datei angefügt wurde, in der ein paar leere Tests enthalten sind, die auf Grundlage des Features erstellt wurden. Mit jedem dieser Tests können die Schritte des Szenarios ausgeführt werden. Die Schritte müssen aber erstellt werden. Dazu gibt es verschiedene Möglichkeiten. Sie können die Tests ausführen und SpecFlow in der Testausgabe die Codelisten für die Steps-Klasse ausgeben lassen, die Sie dann kopieren und einfügen. Alternativ können Sie ein Tool aus dem Kontextmenü der Featuredatei verwenden. Den zweiten Ansatz beschreibe ich jetzt:

  1. Klicken Sie mit der rechten Maustaste auf das Editorfenster der Featuredatei. Im Kontextmenü sehen Sie den Bereich mit den SpecFlow-Aufgaben.
  2. Klicken Sie auf „Generate Step Definitions“. Ein Fenster wird angezeigt, in dem die zu erstellenden Schritte überprüft werden.
  3. Klicken Sie auf die Schaltfläche „Copy methods to clipboard“, und verwenden Sie die Standardwerte.
  4. Erstellen Sie im AddCustomer-Ordner des Projekts eine neue Klassendatei namens „Steps.cs“.
  5. Öffnen Sie die Datei, und fügen Sie den Inhalt der Zwischenablage in die Klassendefinition ein.
  6. Fügen Sie mithilfe von TechTalk.SpecFlow am Anfang der Datei ein Namespaceverweis hinzu.
  7. Fügen Sie der Klasse eine Bindungsanmerkung hinzu.

Die neue Klasse ist in Abbildung 2 aufgeführt.

Abbildung 2: Die Steps.cs-Datei

[Binding]
public class Steps
{
  [Given(@"a user has entered information about a customer")]
  public void GivenAUserHasEnteredInformationAboutACustomer()
  {
    ScenarioContext.Current.Pending();
  }
  [Given(@"she has provided a first name and a last name as required")]
  public void GivenSheHasProvidedAFirstNameAndALastNameAsRequired
 ()
  {
    ScenarioContext.Current.Pending();
  }
    [When(@"she completes entering more information")]
  public void WhenSheCompletesEnteringMoreInformation()
  {
    ScenarioContext.Current.Pending();
  }
  [Then(@"that customer should be stored in the system")]
  public void ThenThatCustomerShouldBeStoredInTheSystem()
  {
    ScenarioContext.Current.Pending();
  }
  [Given(@"she has not provided both the firstname and lastname")]
  public void GivenSheHasNotProvidedBothTheFirstnameAndLastname()
  {
    ScenarioContext.Current.Pending();
  }
  [Then(@"that user will get a message")]
  public void ThenThatUserWillGetAMessage()
  {
    ScenarioContext.Current.Pending();
  }
  [Then(@"the customer will not be stored into the system")]
  public void ThenTheCustomerWillNotBeStoredIntoTheSystem()
  {
    ScenarioContext.Current.Pending();
  }
}

Wenn Sie sich die beiden von mir erstellten Szenarien ansehen, werden Sie bemerken, dass trotz einiger Übereinstimmungen in der Definition (wie „ein Benutzer hat Informationen zu einem Kunden eingegeben“), von den generierten Methoden keine doppelten Schritte erstellt werden. Zudem ist es bemerkenswert, dass SpecFlow die Konstanten in den Methodenattributen nutzt. Die tatsächlichen Methodennamen sind irrelevant.

An diesem Punkt könnten Sie den Test zum Aufrufen dieser Methoden von SpecFlow ausführen lassen. Obwohl SpecFlow eine Anzahl von Frameworks zu Komponententests unterstützt, verwende ich MSTest. Wenn Sie sich diese Lösung dann in Visual Studio ansehen, können Sie erkennen, dass in der Code-Behind-Datei des Features ein TestMethod-Element für jedes Szenario definiert ist. Von jedem TestMethod-Element wird die korrekte Kombination von Step-Methoden und einem TestMethod-Element ausgeführt, das für das HappyPath-Szenario ausgeführt wird.

Wenn ich das jetzt so anwende, indem ich die Featuredatei mit der rechten Maustaste anklicke und „Run SpecFlow Scenarios“ auswähle, bleibt der Test ergebnislos und die folgende Meldung wird angezeigt: „One or more step definitions are not implemented yet.“ Das passiert, weil alle Methoden in der Steps-Datei noch immer „Scenario.Current.Pending“ aufrufen.

Also ist es an der Zeit, die Methoden auszuarbeiten. In meinen Szenarien wird deutlich, dass ich einen Customer-Typ mit bestimmten erforderlichen Daten benötige. Dank anderer Dokumentation ist mir bekannt, dass zurzeit der Vorname und der Nachname notwendig sind, also benötige ich diese beiden Eigenschaften im Customer-Typ. Ich brauche außerdem einen Mechanismus zum Speichern der Kundendaten und einen Ort, an dem ich sie speichern kann. Für meine Tests ist es unerheblich, wie und wo das geschieht. Es ist nur wichtig, dass es gemacht wird. Daher verwende ich für das Abrufen und Speichern der Daten ein Repository.

Zu Beginn füge ich meiner Steps-Klasse die Variablen „_customer“ und „_repository“ hinzu:

private Customer _customer;
private Repository _repository;

Und dann erstelle ich den Abriss einer Customer-Klasse:

public class Customer
{
  public int Id { get; set; }
  public string FirstName { get; set; }
  public string LastName { get; set; }
}

Das genügt, um meinen step-Methoden etwas Code hinzuzufügen. In Abbildung 3 wird die Logik dargestellt, die den mit HappyPath in Beziehung stehenden Schritten hinzugefügt wurde. In einem Schritt erstelle ich einen neuen Kunden und gebe Vor- und Nachname im nächsten Schritt an. Es gibt wirklich nichts mehr am WhenSheCompletesEnteringMoreInformation-Schritt zu verfeinern.

Abbildung 3: Einige Step-Methoden in SpecFlow

[Given(@"a user has entered information about a customer")]
public void GivenAUserHasEnteredInformationAboutACustomer()
{
  _newCustomer = new Customer();
}
[Given(@"she has provided a first name and a last name as required")]
public void GivenSheHasProvidedTheRequiredData()
{
  _newCustomer.FirstName = "Julie";
  _newCustomer.LastName = "Lerman";
}
[When(@"she completes entering more information")]
public void WhenSheCompletesEnteringMoreInformation()
{
}

Der letzte Schritt ist am interessantesten. An diesem Punkt speichere ich die Kundendaten nicht nur, sondern ich bestätige auch, dass sie tatsächlich gespeichert wurden. Ich benötige in meinem Repository eine Add-Methode zum Speichern der Kundendaten, eine Speichern-Methode, um die Daten in die Datenbank zu verschieben und dann eine Möglichkeit, um festzustellen, ob die Daten des Kunden tatsächlich vom Repository gefunden werden können. Also füge ich meinem Repository folgendermaßen eine Add-Methode sowie eine Speichern- und eine FindById-Methode hinzu:

public class CustomerRepository
{
  public void Add(Customer customer)
    { throw new NotImplementedException();  }
  public int Save()
    { throw new NotImplementedException();  }
  public Customer FindById(int id)
    { throw new NotImplementedException();  }
}

Jetzt kann ich meinem letzten vom HappyPath-Szenario aufgerufenen Schritt die erforderliche Logik hinzufügen. Ich füge dem Repository die Kundendaten hinzu und führe einen Test aus, um zu prüfen, ob die Daten im Repository gefunden werden können. An dieser Stelle verwende ich endlich eine Behauptung, um festzustellen, ob mein Szenario erfolgreich ist. Wenn die Kundedaten gefunden werden (d. h. IsNotNull), war der Test erfolgreich. Das ist ein häufig verwendetes Muster, mit dem getestet wird, ob Daten gespeichert wurden. Allerdings bemerke ich aufgrund meiner Erfahrungen mit Entity Framework ein Problem, das vom Test nicht erkannt wird. Ich beginne mit folgendem Code, um Ihnen das Problem auf eine einprägsamere Weise darzulegen, als Ihnen einfach von Anfang an die richtige Methode vorzuführen (ich meine folgende Möglichkeit):

[Then(@"that customer should be stored in the system")]
public void ThenThatCustomerShouldBeStoredInTheSystem()
{
  _repository = new CustomerRepository();
  _repository.Add(_newCustomer);
  _repository.Save();
  Assert.IsNotNull(_repository.FindById(_newCustomer.Id));
}

Wenn ich meinen HappyPath-Test erneut ausführe, wird ein Fehler verursacht. Sie können in Abbildung 4 sehen, dass die bisherige Funktionsweise meines SpecFlow-Szenarios in der Testausgabe dargestellt wird. Achten Sie aber auf den Grund für den Fehler beim Test. Er wird nicht etwa verursacht, weil „FindById“ die Kundendaten nicht finden konnte, sondern weil meine Repository-Methoden noch gar nicht implementiert sind.

Abbildung 4: In der Ausgabe des fehlerhaften Test wird der Status für jeden Schritt angezeigt

Test Name:  HappyPath
Test Outcome:               Failed
Result Message:             
Test method UnitTestProject1.UserStories.Add.AddCustomerFeature.HappyPath threw exception:
System.NotImplementedException: The method or operation is not implemented.
Result StandardOutput:     
Given a user has entered information about a customer
-> done: Steps.GivenAUserHasEnteredInformationAboutACustomer() (0.0s)
And she has provided a first name and a last name as required
-> done: Steps. GivenSheHasProvidedAFirstNameAndALastNameAsRequired() (0.0s)
When she completes entering more information
-> done: Steps.WhenSheCompletesEnteringMoreInformation() (0.0s)
Then that customer should be stored in the system
-> error: The method or operation is not implemented.

Also besteht der nächste Schritt darin, mein Repository mit Logik zu versehen. Schließlich verwende ich das Repository, um mit der Datenbank zu interagieren und, weil ich ein Fan von Entity Framework bin, verwende ich in meinem Repository ein Entity Framework-DbContext. Ich beginne mit der Erstellung einer DbContext-Klasse, mit der ein Customers-DbSet verfügbar gemacht wird:

public class CustomerContext:DbContext
{
  public DbSet<Customer> Customers { get; set; }
}

Dann kann ich „CustomerRepository“ so anpassen, dass „CustomerContext“ für die Persistenz verwendet wird. Bei dieser Demo arbeite ich direkt gegen den Kontext, anstatt mir Gedanken über Abstraktionen zu machen. Im Folgenden sehen Sie die aktualisierte „CustomerRepository“:

public  class CustomerRepository
{
  private CustomerContext _context = new CustomerContext();
  public void Add(Customer customer
  {    _context.Customers.Add(customer);  }
  public int Save()
  {    return _context.SaveChanges();  }
  public Customer FindById(int id)
  {    return _context.Customers.Find(id);  }
}

Wenn ich den HappyPath-Test jetzt wiederhole, verläuft er erfolgreich und meine Schritte werden als „Fertig“ markiert. Aber ich bin mit dem Ergebnis noch nicht zufrieden.

Sicherstellen, dass EF-Verhalten bei Integrationstests interpretiert wird

Warum bin ich noch nicht zufrieden, obwohl mein Test erfolgreich war und ich den hübschen grünen Kreis zu sehen bekomme? Das ist so, weil ich weiß, dass der Test das Speichern der Kundendaten nicht wirklich sicherstellt.

Kommentieren Sie den Aufruf zum Speichern aus der ThenThatCustomerShouldBeStoredInTheSystem-Methode heraus, und führen Sie den Test noch einmal aus. Er verläuft immer noch erfolgreich. Dabei wurden die Kundendaten noch nicht einmal in der Datenbank gespeichert! Bemerken Sie jetzt, dass es hier eigenartig riecht? So riecht ein sogenanntes „falsch positives Ergebnis“.

Das Problem liegt in der in meinem Repository verwendeten DbSet Find-Methode. Dabei handelt es sich um eine spezielle Methode im Entity Framework, bei der zunächst die im Arbeitsspeicher befindlichen und vom Kontext nachverfolgten Objekte geprüft werden, bevor auf die Datenbank übergegangen wird. Durch den Aufruf von „Add“, wurde „CustomerContext“ auf die Kundeninstanz aufmerksam. Durch den Aufruf von „Customers.Find“ wurde die Instanz entdeckt und eine weitere Überprüfung der Datenbank wurde unterlassen. Tatsächlich ist die ID des Kunden weiterhin 0 (null), da sie ja bisher nicht gespeichert wurde.

Da ich Entity Framework verwende (und Sie sollten das Verhalten jedes von Ihnen verwendeten ORM-Frameworks bedenken), lässt sich einfacher testen, ob die Kundendaten wirklich in die Datenbank übernommen wurden. Wenn die Kundendaten mithilfe der EF-SaveChanges-Anweisung in die Datenbank einfügt werden, wird die von der Datenbank neu erstellte ID des Kunden zurückgeben und auf die einfügende Instanz angewendet. Daher weiß ich, sobald die neue Kunden-ID nicht länger den Wert 0 (null) aufweist, dass die Kundendaten diesmal tatsächlich in der Datenbank gespeichert wurden. Ich muss die Datenbank nicht erneut abfragen.

Ich ändere die Assertion für die Methode einfach entsprechend ab. Im Folgenden sehen Sie die Methode, mit der ein wirklich erfolgreicher Test durchgeführt werden kann.

[Then(@"that customer should be stored in the system")]
  public void ThenThatCustomerShouldBeStoredInTheSystem()
  {
    _repository = new CustomerRepository();
    _repository.Add(_newCustomer);
    _repository.Save();
    Assert.IsNotNull(_newCustomer.Id>0);
  }

Er ist erfolgreich, und er ist es aus den richtigen Gründen. Es ist gar nicht so ungewöhnlich, dass ein fehlerhafter Test definiert wird. Zum Beispiel die Verwendung von Assert.IsNull(FindById(customer.Id), um sicherzustellen, dass der Test nicht aus den falschen Gründen erfolgreich ist. Doch in diesem Fall hätte sich das Problem nicht gezeigt, bis der Save-Aufruf entfernt worden wäre. Wenn Sie sich der Funktionsweise von EF noch unsicher sind, mag es sinnvoll sein, auch einige Integrationstests zu erstellen, die mit User Storys nichts zu tun haben, um sicherzugehen, dass sich die Repositorys erwartungsgemäß verhalten.

Verhaltenstest oder Integrationstest?

Als ich die Lernkurve zur Ausarbeitung dieses ersten SpecFlow-Szenarios durchlaufen habe, hatte ich den Eindruck, mich damit auf recht dünnes Eis zu begeben. In meinem Szenario wird angegeben, dass die Kundendaten „im System“ gespeichert werden sollen.

Mein Problem dabei ist, dass mich die Definition von „System“ nicht sonderlich überzeugt hat. Dank meines Hintergrunds weiß ich, dass die Datenbank oder zumindest ein Persistenzmechanismus einen ausgesprochen wichtigen Teil des Systems ausmacht.

Der Benutzer schert sich nicht um Repositorys und Datenbanken, ihm geht es nur um die Anwendung. Allerdings wäre er gar nicht erfreut darüber, diesen besagten Kunden nach Wiederanmeldung an der Anwendung plötzlich nicht mehr finden zu können, da die Kundendaten nie wirklich in der Datenbank gespeichert wurden (nur weil ich nicht bedacht habe, „_repository.Save“ sei erforderlich, um dem Szenario gerecht zu werden).

Ich habe mit einem anderen Dennis, Dennis Doomen, dem Autor von „Fluent Assertions“ und ein Fachmann für BDD, TDD und große Unternehmenssysteme, darüber gesprochen. Er hat mir bestätigt, dass ich als Entwickler mein Wissen auf jeden Fall in die Schritte und Tests einbringen sollte, selbst wenn dies bedeuten würde, sich über die Intention des Benutzers, der das ursprüngliche Szenario definiert hat, hinauszubewegen. Benutzer stellen ihr Wissen bereit, und ich füge meins so hinzu, dass ich dem Benutzer meine technische Sichtweise nicht gleich unter die Nase reibe. Ich spreche weiterhin seine Sprache und kommuniziere mit ihm.

Sich eingehender mit BDD und SpecFlow befassen

Ich bin ziemlich sicher, dass mir das Einarbeiten in BDD nicht so leicht gefallen wäre, wenn nicht bereits so viele Tools zur Unterstützung vorhanden wären. Obwohl ich ein Datenfreak bin, liegt mir die Arbeit mit Kunden sehr am Herzen, ich möchte ihr Geschäft verstehen und sicherstellen, dass sie bei der Verwendung der von mir erstellten Software ein gutes Gefühl haben. Deshalb sprechen mich der domänengesteuerte und der verhaltensgesteuerte Entwurf so sehr an. Ich glaube, dass viele Entwickler ebenso denken (wenn auch vielleicht nur tief im Innern) und sich gleichermaßen von diesen Techniken inspiriert fühlen.

Abgesehen von den Freunden, die mir bis zu diesem Punkt eine große Hilfe waren, fand ich vor allem die folgenden Ressourcen sehr nützlich. Der Artikel „Verhaltensgesteuerte Entwicklung mit SpecFlow und WatiN“ im MSDN Magazin, den Sie unter msdn.microsoft.com/magazine/gg490346 finden können, war ebenfalls sehr hilfreich. Ich habe mir auch ein hervorragendes Modul aus David Starrs Kurs zur testgesteuerten Entwicklung auf Pluralsight.com angesehen. (Tatsächlich habe ich es mir mehrmals angesehen.) Ich fand auch den Wikipedia-Artikel zu BDD (bit.ly/LCgkxf) recht interessant, da darin die Gesamtheit der Geschichte der verhaltensgesteuerten Entwicklung dargestellt und aufgezeigt wird, wo BDD zwischen den anderen Verfahren einzuordnen ist. Außerdem bin ich schon sehr gespannt auf das Buch „BDD and Cucumber“, bei dem Paul Rayner (der mir hier beratend zur Seite stand) Co-Autor war.

Julie Lerman ist Microsoft MVP, .NET-Mentor und Unternehmensberaterin und lebt in den Bergen von Vermont. Sie hält bei User Groups und Konferenzen in der ganzen Welt Vorträge zum Thema Datenzugriff und anderen Microsoft .NET-Themen. Julie Lerman führt unter thedatafarm.com/blog einen Blog. Sie ist die Verfasserin von „Programming Entity Framework“ (2010) sowie der Ausgaben „Code First“ (2011) und „DbContext“ (2012). Alle Ausgaben sind im Verlag O’Reilly Media erschienen. Folgen Sie ihr auf Twitter unter twitter.com/julielerman.

Unser Dank gilt den folgenden technischen Experten für die Durchsicht dieses Artikels: Dennis Doomen (Aviva Solutions) und Paul Rayner (Virtual Genius)
Dennis Doomen ist Principal Consultant bei Aviva Solutions (Niederlande). Er hält gelegentlich Vorträge und ist Trainer für agiles Programmieren, Autor von Codierungsrichtlinien für C# 3.0, 4.0 und 5.0, vom „Fluent Assertion Framework“ und dem „Silverlight Cookbook“. Zurzeit erstellt er auf Grundlage von .NET, Event Sourcing und CQRS Lösungen auf Unternehmensebene. Er liebt agile Entwicklung, Architektur, Xtreme-Programmierung und domänengesteuerte Entwicklung. Sie erreichen ihn auf Twitter über @ddoomen.