2011-01-05 13 views
3

Ich benutze specflow, um meine App zu spezifizieren, und es rettete mich nur von wirklich schlechten Sachen, so dass ich es wirklich mag :-) Allerdings habe ich ein Problem mit der Kopplung zwischen den Schritten : Zum Beispiel, weil ich Mocks schwer in einem Schritt verwende, sage ich dem Mock, dass es eine Entität zurückgeben sollte, aber in einem anderen erzähle ich dem Mock, dieselbe Entität zurückzugeben, aber mit einer anderen Eigenschaft.Starke Kopplung zwischen den Schritten in Gurke specs

Schauen Sie in diesem Schritt (von Darrens Antwort gestohlen unten und modifiziert):

Given a guy the following guy exists: 
| Name  | Age | Salary | 
| John Doe | 42 | 400 | 
When his salary changes to 420 
And I run the paycheck program 
Then he should be paid 420 

hier sehe ich mit Guy Objekt beginnen und das Objekt später ändern - das ist die Sache, die ich ist die Prüfung bin.

Also habe ich eine Entity in das Mock Repository gestellt, dann ziehe ich es in einem anderen Schritt heraus und stecke es wieder ein. Wie vermeiden Sie eine hohe Kopplung und Wiederverwendbarkeit zwischen einzelnen Schritten?

Natürlich könnte ich eine lokale Variable in der Szenario-Klasse behalten und alle Entitäten in diese Variable setzen, aber ich würde dann die Schritte koppeln.

Antwort

6

Die Art und Weise, die ich Kopplung vermeiden und Wiederverwendbarkeit fördern ist:

1.) Gruppe meiner Schritte durch das Unternehmen, wie AccountRepositorySteps (für AccountRepository) oder AccountControllerSteps (für Account).

2.) Machen Sie die Schritte abhängig von Abstraktionen, nicht von Betonen (genau wie wir es mit unserem Produktionscode tun würden).

3.) Lean auf dem aktuellen ScenarioContext, um Werte zwischen Schritten und Schrittdateien zu übergeben.

Hier ist ein kurzes Beispiel:

Given a guy with the name Darren exists 
And a guy with the name John exists 
When I hit the guy page 
Then I should see two guys 

RepositorySteps.cs

private List<string> guys; 

[BeforeScenario] 
public void Setup(){ 

    guys = new List<string>(); 

    var fake = new Mock<IRepository>(); 

    fake.Setup(x=>x.GetGuys()).Returns(guys); 

    ScenarioContext.Current.Set(fake) // Mock<IRepository> 
    ScenarioContext.Current.Set(fake.Object); // IRepository 
} 

[Given("a guy with the name '(.*)' exists"] 
public void a(string guy){ 
    guys.Add(guy); 

    // and if I need to pull out the mock, I can do it like so 
    var fake = ScenarioContext.Current.Get<Mock<IRepository>>(); 
} 

GuyController.cs

When["I hit the guy page"] 
public void x(){ 
    var repository = ScenarioContext.Current.Get<IRepository>(); 
    var controller = new GuyController(repository); 

    var result = controller.Index(); 
    ScenarioContext.Current.Set(result); 
} 

See, hier der Schritt für die GuyController bekommt das Mock-Objekt, aber Er weiß nicht, dass es ein Spott ist. Es ist nur ein IR-Repository für ihn. Und wenn Sie aus irgendeinem Grund das REAL-Repository für das IRepository laden und Ihre Spezifikationen ausführen möchten, müssen Sie den ScenarioContext nur mit dem realen IRepository laden.

Nach diesem Muster sind meine Schritte sehr entkoppelt und geschützt vor Änderungen, die ich an andere mache. Es funktioniert viel besser als die Tricks, die ich zu Beginn meiner Arbeit mit SpecFlow gemacht habe, wo ich statische Methoden oder nicht zusammenhängende Schritte in derselben Schrittdatei verwenden würde.

0

Ich frage mich, ob es besser wäre, das Verhalten zu teilen.

Scenario: Change Salary 

Given a guy the following guy exists: 
| Name  | Age | Salary | 
| John Doe | 42 | 400 | 
When his salary changes to 420 
Then his salary should be 420 

Und ...

Scenario: Pay Guy 

Given a guy the following guy exists: 
| Name  | Age | Salary | 
| John Doe | 42 | 400 | 
And I run the paycheck program 
Then he should be paid 400 

Sie sind separate Einheiten des Verhaltens.

In Bezug auf den geteilten Kontext ist die beste Lösung, die ich je gesehen habe, die Abhängigkeitsinjektion. Erstellen Sie eine SharedContext-Klasse, und injizieren Sie sie in die Step-Definition-Klassen, die den freigegebenen Kontext benötigen.Auf diese Weise können Sie Ihre Schrittdefinitionsdateien beliebig teilen, und sie können den Kontext freigeben. Zahlreiche Tools verfügen über eine einfache IoC-Container-Funktionalität (z. B. SpecFlow).

class SharedContext 
{ 
object MyObject1 {get; set;} 
object MyObject2 {get; set;} 
//Etc. 
} 

class StepDefinitions1 
{ 

private SharedContext _context; 

public Stepdefinitions1(SharedContext context) 
{ 
this._context = context; 
}  
//Now use this._context.Properties to get at the shared objects in your 
//step definitions  
} 

Der Container wird sich um den Rest kümmern.

Objektlebenszyklus der SharedContext-Klasse ist ein einzelnes Szenario. I.e. Für jedes neue Szenario wird ein neuer SharedContext erstellt und über den Konstruktor an alle Schritte in Klassen weitergegeben, die darauf verweisen, bis der letzte "Dann" -Schritt ausgeführt wurde.

+0

Und oops! Alter Beitrag. –