代码之家  ›  专栏  ›  技术社区  ›  David Kiff

有些事情你就是不能测试?

  •  2
  • David Kiff  · 技术社区  · 17 年前

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

    如果我们有一个直接依赖于.NET框架的类,即system.io.file类,那么我们不能单独测试它。

    当然,我们可以包装它并将其注入到依赖类中,但随后我们开始包装每个.NET类!如果包装器不是直接调用呢?也许它首先做了一些检查/业务逻辑,您想测试它?

    目前,我们总结到一定的级别,然后就不需要麻烦单元测试这个包装器了。也许这是可以的,因为它将在稍后通过集成和探索性测试进行测试?

    下面是C中的一个示例,以说明我的观点:

    此类与.NET框架紧密耦合。很好,但是现在我不能单独测试它,它需要文件等。

    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 回复  |  直到 17 年前
        1
  •  1
  •   Wim Coenen    17 年前

    实际上,在某个时刻,您必须实现如下接口 IFileSystem 从而将应用程序粘到.NET框架上。这个“中间的粘合代码”不能进行单元测试。只要粘合代码非常简单,这不是什么问题。

    如果粘合代码没有您想要的那么简单,您仍然可以这样做 自动化集成测试 例如,以某种自我测试模式运行已组装的应用程序。

    单元测试更受欢迎的原因是它们更容易创建,也不那么脆弱。运行单元测试不需要准备数据库、Web服务器或文件系统。如果你改变了B类,你不需要担心A类中断的单元测试。集成测试可以测试更多的东西,但是它们没有这些优势。

        2
  •  5
  •   Amber    17 年前

    一般来说,像.NET这样的测试框架不是你的责任,而是发布框架的人的责任。

        3
  •  1
  •   Matt Howells    17 年前

    不需要消除您对整个框架的依赖性——只需要那些妨碍您对代码进行单元测试的部分。因此,我认为您可以随意终止system.io.file操作以及数据库调用。我还发现有一个IClock接口来告诉时间而不是调用datetime.utcnow非常有用——这使我们能够在单元测试中控制时间的推移。

        4
  •  1
  •   Michaël Larouche    17 年前

    这是单元测试和集成测试之间区别的一个很好的例子。为了 单元测试 您的路径解析器,您需要传递一个手工创建的模拟对象或使用模拟框架(如我最喜欢的 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”的单独项目和测试系统文件系统。

    集成测试应该如下所示

    [Test]
    public void ShouldCombinePath()
    {
        DotNetFileSystem fileSystem = new DotNetFileSystem();
    
        string resultPath = fileSystem.Combine("Test.filename");
    
        Assert.That(resultPath, Text.Contains("@D:\MyExample\Test.filename"));
    }
    

    集成测试通常在使用持续集成软件在新提交上创建软件构建时调用。它们可能很慢,因为它们使用外部依赖项。

        5
  •  0
  •   Ray    17 年前

    是否要测试System.io?如果不是,为什么不使用mocking/stubing方法来消除依赖?

        6
  •  0
  •   pauljwilliams    17 年前

    如果使用依赖注入,则可以确保类的外部调用指向存根,而不是外部库。

        7
  •  0
  •   AutomatedTester    17 年前

    不幸的是,在单元测试时不能测试应用程序中的所有内容。这就是为什么当测试的目标是让测试在项目中覆盖90%的代码。

    将此测试作为集成测试的一部分,甚至作为端到端测试的一部分,因为测试的操作将非常昂贵

        8
  •  0
  •   Kevin Vaughan    17 年前

    您只需要测试您的类,而不需要下面的框架。

    为此,您需要一个设置测试用例的测试驱动程序,在您的示例中,创建一个文件来解析、实例化您的类并让它完成它的工作。

    一个好的测试驱动程序也会清理测试场景,并使系统保持与测试前相同的状态。

        9
  •  0
  •   Chris Missal    17 年前

    如果你不能测试一个类,但是你可以使用一个隔离框架来模拟它(Rhino模拟、typemock、moq),你可以伪造出这个“不稳定”类的细节。

    推荐文章