3

我正在使用 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 对象开始,然后修改该对象 - 这是我正在测试的东西。

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

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

4

2 回答 2

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

存储库步骤.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 时使用的技巧要好得多,我会在同一个步骤文件中使用静态方法或将不相关的步骤分组。

于 2011-01-05T13:10:15.773 回答
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”步骤。

于 2012-09-27T11:16:50.703 回答