Java 单元测试类的最佳实践,该类主要负责调用依赖项的方法,但也包含逻辑
让我们假设我有Java 单元测试类的最佳实践,该类主要负责调用依赖项的方法,但也包含逻辑,java,c#,unit-testing,testing,integration-testing,Java,C#,Unit Testing,Testing,Integration Testing,让我们假设我有StartCommandHandler,它负责创建一些包含所需文件的文件。但要做到这一点,我必须给他一系列的子责任,比如: 检查FTP中是否存在文件 如果没有,则将文件从多个源下载到临时文件夹 然后执行文件夹中的一些脚本 然后在脚本执行后读取生成的文件 然后从该文件夹创建zip 然后删除该文件夹 然后更新数据库 作为该命令处理程序的结果,我们正在创建包含所有必需文件的文件夹。现在,该文件夹已准备好进行其他操作 我刚刚读了“单元测试的艺术”。并开始添加单元测试。我也遵循了坚实的原
StartCommandHandler
,它负责创建一些包含所需文件的文件。但要做到这一点,我必须给他一系列的子责任,比如:
- 检查FTP中是否存在文件
- 如果没有,则将文件从多个源下载到临时文件夹
- 然后执行文件夹中的一些脚本
- 然后在脚本执行后读取生成的文件
- 然后从该文件夹创建zip
- 然后删除该文件夹
- 然后更新数据库
“单元测试的艺术”
。并开始添加单元测试。我也遵循了坚实的原则。特别是,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();
}
我已经测试了该命令处理程序的所有依赖项。但是,hat命令处理程序还包括一些小逻辑,例如,是否需要下载文件,或者是否删除临时文件等等
我脑子里有很多问题,比如:
deletemp
,脚本是否执行,或者脚本结果是否以正确的方式传递给SetScriptResult
方法。它是好的单元测试吗您也可以对这种方法使用单元测试。这可以通过模拟依赖项来实现。原则如下: 使用预定义的结果创建依赖项的模拟(通常通过实现依赖项的接口)。例如,您为
dependency2
提供了一个实现,该实现总是为IsScriptNeeded()
返回true
然后监视dependency3
并检查它是否被调用(应该是!),以及dependency4.SetScriptResult(…)
的参数是否与结果匹配
对于C#,有像FakeiTasy和Moq这样的库,这使得模仿变得轻而易举。使用xUnit和Fakeetisy的示例代码段:
使用系统;
使用假货;
使用Xunit;
公共类MyTest
{
[事实]
公开无效测试()
{
//创建模拟
var mock=A.Fake();
A.CallTo(()=>mock.DoSomething()).Returns(true);
//创建要测试的类
var sut=新系统未测试(模拟);
sut.DoSomethingElse();
//断言调用了mock.DoSomething()
A.CallTo(()=>mock.DoSomething()).musthavecoursed();
}
}
它是好的单元测试吗
恐怕你得自己决定这个话题了
据我所知,并不是每个人都同意UT应该涵盖哪些内容。我想说,这也取决于你的团队、你的公司以及你真正想要实现的目标
- 有些人盲目地测试一切,不管发生什么,因为你永远不知道下一个bug会是什么。他们可能是对的。 在您的案例中,他们将对每个依赖项进行多个模拟,以测试通过和未通过的案例
- 有些人认为它太昂贵,而且无法证明对高级协调类的测试,就像您在这里指出的那样。他们可能也是对的
// Explicitly verify each expectation...
mockSomeClass.Verify(s => s. SetScriptResult(It.IsAny<YourType>()/* or whatever value parameters you may setup snd ecpect it to be called*/), Times.Once());
//显式验证每个期望值。。。
验证(s=>s.SetScriptResult(It.IsAny()/*或您可能设置的任何值参数,并希望调用它*/),Times.Once());
我相信您在这里所做的和提到的对于单元测试来说已经足够了,因为单元测试的责任是测试一个单元,假设其他互连单元是
_dependency3.Archive();
_dependency5.DeleteTemp();
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());
}
}