当进行单元测试时,总是很难知道你去的框架有多低。
如果我们有一个直接依赖于.NET Framework的类,即System.IO.File类,那么我们无法真正地单独测试它。
当然我们可以将它包装并将其注入依赖类,但随后我们开始包装每个.NET类!如果那个包装不是直接通话怎么办?也许它首先做一些检查/业务逻辑,你想要测试?
目前我们已经将其包装到某个级别,然后就不用担心单元测试那个包装器了。也许这没关系,因为它将在稍后通过集成和探索性测试进行测试?
这是C#中的一个例子,只是为了说明我的观点:
这个类与.NET Framework紧密耦合..很好,但现在我无法单独测试它,它需要存在文件等。
public class PathResolver
{
public string Resolve(string filename)
{
string completePath = string.Empty;
if(!File.Exists(filename))
{
return Path.Combine(@"D:\MyExample", filename);
}
return completePath;
}
}
我们可以通过以下方式对此进行单元测试:
public class PathResolver
{
private readonly IFileSystem _fileSystem;
public PathResolver(IFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
public string Resolve(string filename)
{
string completePath = string.Empty;
if(!_fileSystem.Exists(filename))
{
return _fileSystem.Combine(@"D:\MyExample", filename);
}
return completePath;
}
}
但现在我们无法测试“FileSystem”类了!
其他人的想法是什么?
答案 0 :(得分:5)
一般来说,像.NET这样的测试框架不是你的责任,它是发布框架的人的责任。
答案 1 :(得分:1)
没有必要在整个框架上存在依赖关系 - 只是那些妨碍你对代码进行单元测试的部分。所以我认为您应该随意删除System.IO.File操作以及数据库调用。我还发现让IClock接口告诉时间而不是调用DateTime.UtcNow非常有用 - 这使我们能够控制单元测试中的时间流逝。
答案 2 :(得分:1)
实际上,在某些时候你必须实现像IFileSystem
这样的接口,从而将你的应用程序粘贴到.NET框架上。这种“胶水代码介于两者之间”不能进行单元测试。只要胶水代码非常简单,这就没什么问题了。
如果胶水代码不是您想要的那么简单,您仍然可以进行自动化集成测试,例如以某种自检模式运行您组装的应用程序。
单元测试更受欢迎的原因是它们更易于创建且不易碎。您无需准备数据库,Web服务器或文件系统即可运行单元测试。如果你改变了B类,你不必担心A类的单元测试会中断。集成测试可以测试更多的东西,但它们没有这些优点。
答案 3 :(得分:1)
这是单元测试和集成测试之间差异的一个很好的例子。对于单元测试您的PathResolver,您需要传递手工创建的模拟对象或使用模拟框架(例如我最喜欢的Moq)。使用模拟对象,您将能够测试是否调用了Combine方法。
单元测试看起来像这样(使用Moq):
[Test]
public void ShouldCombinePath()
{
IFileSystem fs = new Mock<IFileSystem>();
PathResolver resolver = new PathResolver(fs.Object);
resolver.Resolve("Test.filename");
fs.Verify(fs => fs.Combine());
}
单元测试应该在没有外部依赖性的情况下快速执行。应该在每次编译时调用它们。
但你是对的,你还需要测试具体的课程。这就是我们所说的集成测试。我建议你创建一个名为“MyProject.IntegrationTest”的独立项目,并使用项目中包含的测试文件直接测试SystemFileSystem。
集成测试应如下所示
[Test]
public void ShouldCombinePath()
{
DotNetFileSystem fileSystem = new DotNetFileSystem();
string resultPath = fileSystem.Combine("Test.filename");
Assert.That(resultPath, Text.Contains("@D:\MyExample\Test.filename"));
}
在使用持续集成软件在新提交上创建软件构建时,通常会调用集成测试。它们可能很慢,因为它们使用外部依赖。
答案 4 :(得分:0)
您想测试System.IO吗?如果没有,为什么不使用模拟/存根方法来摆脱依赖?
答案 5 :(得分:0)
如果使用依赖注入,则可以确保将类的外部调用定向到存根,而不是外部库。
答案 6 :(得分:0)
不幸的是,在单元测试时,您无法测试应用程序中的所有内容。这就是为什么在测试时你的目标是让你的测试在整个项目中实现90%的代码覆盖率。
将此测试作为集成测试的一部分,甚至是端到端测试的一部分,因为测试此操作的行为相当昂贵
答案 7 :(得分:0)
您只需要测试您的课程,而不是下面的框架。
为此你需要一个设置测试用例的testdriver,在你的例子中创建一个文件来解析,实例化你的类并让它完成它的工作。
一个好的testdriver也会清理测试场景,并使系统处于与测试前相同的状态。
答案 8 :(得分:0)
如果你不能测试一个类,但你可以使用隔离框架(Rhino Mocks,Typemock,Moq)来模拟它,你可以伪造出那个“不可测试”类的细节。