Ezi*_*nze 5 c# java testing integration-testing unit-testing
假设我有StartCommandHandler责任创建一些包含所需文件的文件。但为了做到这一点,我必须赋予他一系列子职责,例如:
由于该命令处理程序,我们正在创建包含所有必需文件的文件夹。现在该文件夹已准备好进行其他操作。
我刚刚读过"Art of the Unit testing"。并开始添加单元测试。SOLID我也遵循原则。特别是SRP和DIP,我认为这是单元测试的先决条件。所以,我上面所说的大部分事情都是通过特定的接口完成的。因此,该命令处理程序 90% 的工作是调用依赖项的方法。10%是这样的逻辑:
if(!_dependency1.IsAnySomething())
{
_dependency2.Download();
var isScriptNeeded = _dependency2.IsScriptNeeded();
if(isScriptNeeded)
{
var res = _dependency3.ExecuteScript();
_dependency4.SetScriptResult(res.Info, res.Date, res.State);
}
_dependency3.Archive();
_dependency5.DeleteTemp();
}
Run Code Online (Sandbox Code Playgroud)
我已经测试了该命令处理程序的所有依赖项。但是,帽子命令处理程序还包括一些小逻辑,例如是否需要下载文件,或者是否删除临时文件等等......
我脑子里有很多问题,比如:
DeleteTemp下载后是否调用,或者脚本是否执行,或者脚本结果以正确的方式传递给方法SetScriptResult。这是好的单元测试吗?单元测试应该测试代码的行为,而不是代码的实现。
考虑单元测试如何增加价值是有帮助的:它们传达代码的预期行为,并验证预期行为是否是由实现生成的。它们在项目生命周期中两次增加价值:第一次是在代码最初实现时,第二次是在代码重构时。
但是,如果单元测试与特定实现紧密相关,那么它们在重构时就无法增加价值。
这从来都不是一门完美的科学,但了解您是否正在测试行为或实现的一种方法是问“如果我重构,这个单元测试会中断吗?” 如果重构会破坏测试,那么它就不是一个好的单元测试。
编写单元测试来简单地确保先调用方法 A,然后调用方法 B,然后调用方法 C(或其他)通常没有什么帮助。这只是为了测试您的实现是否是您的实现,并且它可能会阻碍而不是帮助下一个想要重构代码的开发人员。
相反,请考虑行为以及代码如何与其他对象交互。尝试将每个行为分成单独的对象,并单独测试这些对象。
例如,您可以将上述代码分解为三种不同的行为:
每个对象都有单独可测试的行为:
class Cache {
Cache(ValueStore store, ValueFactory factory) { ... }
object GetValue(object key) {
if (!store.HasValue(key))
factory.CreateValue(key);
return store.GetValue(key);
}
}
class CacheTest {
void GetValue_CallsFactory_WhenValueNotInStore() {
// arrange
var store = Mock.Of<VaueStore>(_ => _.HasValue() == false);
var factory = Mock.Of<ValueFactory>();
var cache = new Cache(store, factory);
// act
cache.getValue();
// assert
Mock.Get(factory).Verify(_ => _.CreateValue(), Times.Once());
}
}
Run Code Online (Sandbox Code Playgroud)
您可以对工厂和构建器进行类似的细分,并单独测试它们的行为。