该测试应该是:1)可读性,2)易于编写/维护。 “可读”是什么意思?这意味着它最大限度地减少了跳过代码的次数,以了解特定代码行上正在做什么。
考虑创建测试项目中的工厂。我认为这是为测试安排是可读的最好的方法之一:
public static class MyClassFactory
{
public static MyClass Create(ISomeService serviceA, ISomeOtherService serviceB)
{
return new MyClass(serviceA, serviceB);
}
public static MyClass CreateEmpty()
{
return new MyClass();
}
//Just an example, but in general not really recommended.
//May be acceptable for simple projects, but for large ones
//remembering what does "Default" mean is an unnecessary burden
//and it somehow reduces readability.
public static MyClass CreateWithDefaultServices()
{
return new MyClass(/*...*/);
}
//...
}
(第二个选项将在后面解释)。然后你可以使用这样的:它
[TestMethod]
public void it_should_do_something()
{
//Arrange
var myClass = MyClassFactory.Create(serviceA, serviceB);
//Act
myClass.DoSomething();
//Assert
//...
}
注意如何提高可读性。除了serviceA
和serviceB
,我只留下符合你的榜样,你需要了解的一切就是在这条线上。理想情况下,它看起来像这样:
[TestMethod]
public void it_should_do_something()
{
//Arrange
var myClass = MyClassFactory.Create(
MyServiceFactory.CreateStubWithTemperature(45),
MyOtherServiceFactory.CreateStubWithContents("test string"));
//Act
myClass.DoSomething();
//Assert
//...
}
这提高了可读性。请注意,即使DRY的设置已结束,所有工厂方法都很简单明了。没有必要查看他们的定义 - 他们从一见钟情清楚。
可以缓解写作和维护得到进一步提高?
一个很酷的想法是提供您的测试项目中的扩展方法MyClass
允许可读的配置:
public static MyClassTestExtensions
{
public static MyClass WithService(
this MyClass @this,
ISomeService service)
{
@this.SomeService = service; //or something like this
return @this;
}
public static MyClass WithOtherService(
this MyClass @this,
ISomeOtherService service)
{
@this.SomeOtherService = service; //or something like this
return @this;
}
}
那么你当然像这样使用:
[TestMethod]
public void it_should_do_something()
{
//Arrange
var serviceA = MyServiceFactory.CreateStubWithTemperature(45);
var serviceB = MyOtherServiceFactory.CreateStubWithContents("test string");
var myClass = MyClassFactory
.CreateEmpty()
.WithService(serviceA)
.WithOtherService(serviceB);
//Act
myClass.DoSomething();
//Assert
//...
}
当然中服务不是这种配置的一个好例子(通常,没有它们的对象是不可用的,所以没有提供默认的构造函数),但是对于它提供的任何其他属性一个巨大的优势:使用N个属性进行配置,您可以维护一个干净可读的方式,使用N个扩展方法创建所有可能的配置的测试对象,而不是N x N个工厂方法,并且在构造函数或工厂方法调用中没有N长参数列表。
如果你的类不像上面假设的那么方便,一种可能的解决方案是对它进行子类化,并为子类定义工厂和扩展。
如果它没有意义为对象与它的属性中的一部分存在,他们不能设定你能流利地配置一个工厂,然后创建对象:
//serviceA and serviceB creation
var myClass = MyClassFactory
.WithServiceA(serviceA)
.WithServiceB(serviceB)
.CreateMyClass();
我希望这种工厂的代码很容易推断,所以我不会写在这里,因为这个答案已经看起来有点长了)
我使用TestInitialize的方式,你给第二。正是出于这个原因。我相信如果您的单元测试课程集中并且只测试'一件事'(没有蔓延),那么这种方式可以使测试变得很好并且干净,并且'设置'会被排除在外。我不认为可读性会受到这种干扰。 – CodeBeard