还有一些你无法测试的东西?

时间:2009-08-05 09:48:52

标签: unit-testing frameworks

当进行单元测试时,总是很难知道你去的框架有多低。

如果我们有一个直接依赖于.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”类了!

其他人的想法是什么?

9 个答案:

答案 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)来模拟它,你可以伪造出那个“不可测试”类的细节。