小黄瓜规格中步骤之间的强耦合

时间:2011-01-05 10:33:07

标签: cucumber specflow gherkin

我正在使用specflow来指定我的应用程序,它只是让我免于做坏事,所以我真的很喜欢它:-) 但是我对步骤之间的耦合有问题: 例如,因为我在一步中大量使用模拟,我告诉模拟它应该返回一个实体,但在另一个中我告诉模拟返回相同的实体,但是使用另一个属性。

看看这一步(从以下Darrens的答案中被盗并修改过):

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

看到这里我从一个Guy对象开始,然后修改对象 - 这是我正在测试的东西。

所以我将一个实体放入模拟存储库,然后在另一个步骤中将其拉出并再次放入。 如何避免步骤之间的高耦合和可重用性?

当然,我可以在方案类中保留一个局部变量,并将所有实体放在该变量中,但我会将这些步骤结合起来。

2 个答案:

答案 0 :(得分:6)

我避免耦合和促进可重用性的方式是:

1。)按实体分组我的步骤,如AccountRepositorySteps(对于AccountRepository)或AccountControllerSteps(对于AccountController)。

2。)制作步骤取决于抽象,而不是混凝土(就像我们的生产代码一样)。

3.。)在当前的ScenarioContext上倾斜,以在步骤和步骤文件之间传递值。

这是一个简短的例子:

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);
}

请参阅,这里GuyController的步骤获取该模拟对象,但他不知道这是一个模拟。这只是他的一个IRepository。如果由于某种原因,您需要为IRepository加载REAL存储库并想要运行您的规范,那么您所要做的就是使用真实的IRepository加载ScenarioContext。

遵循这种模式,我的步骤非常分离,并且不受我对其他人所做的更改的影响。它比我在使用SpecFlow时做的技巧要好得多,我在同一步骤文件中使用静态方法或组合无关的步骤。

答案 1 :(得分:0)

我想知道你是否更好地分裂行为。

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

和...

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

他们是独立的行为单位。

关于共享上下文,我遇到的最好的解决方案是依赖注入。创建一些SharedContext类并将其注入需要共享上下文的步骤定义类中。这样,您仍然可以根据需要拆分步骤定义文件,并且可以共享上下文。许多工具都带有简单的IoC容器功能(例如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    
}

容器将负责其余部分。

SharedContext类的对象生命周期是单一场景。即对于每个新场景,创建一个新的SharedContext并通过构造函数传递给引用它的类中的所有步骤,直到执行最后的“Then”步骤。