Unit testing 什么是好的单元测试?

Unit testing 什么是好的单元测试?,unit-testing,language-agnostic,tdd,integration-testing,testing-strategies,Unit Testing,Language Agnostic,Tdd,Integration Testing,Testing Strategies,我相信你们中的大多数人都在编写大量的自动化测试,并且在单元测试时也遇到了一些常见的陷阱 我的问题是,为了避免将来出现问题,你是否遵循了写测试的行为准则?更具体地说:好的单元测试的属性是什么?或者如何编写测试 鼓励不懂语言的建议。记住这些目标(改编自Meszaros的《xUnit测试模式》一书) 测试应该降低风险,而不是降低风险 介绍一下 测试应该易于运行 测试应易于维护 系统围绕着他们发展 让这更容易的一些事情: 测试只会因为以下原因而失败 原因之一 测试应该只测试一件事 最小化测试依赖项

我相信你们中的大多数人都在编写大量的自动化测试,并且在单元测试时也遇到了一些常见的陷阱

我的问题是,为了避免将来出现问题,你是否遵循了写测试的行为准则?更具体地说:好的单元测试的属性是什么?或者如何编写测试

鼓励不懂语言的建议。

记住这些目标(改编自Meszaros的《xUnit测试模式》一书)

  • 测试应该降低风险,而不是降低风险 介绍一下
  • 测试应该易于运行
  • 测试应易于维护 系统围绕着他们发展
让这更容易的一些事情:

  • 测试只会因为以下原因而失败 原因之一
  • 测试应该只测试一件事
  • 最小化测试依赖项(否 对数据库、文件、ui的依赖关系 )
不要忘记,您也可以使用xUnit框架进行集成测试,但要将集成测试和单元测试分开

  • 不要编写巨大的测试。正如“单元测试”中的“单元”所建议的,使每个测试尽可能原子化和隔离。如果必须,请使用模拟对象创建前提条件,而不是手动重新创建太多的典型用户环境
  • 不要测试显然有效的东西。避免测试来自第三方供应商的类,尤其是提供您所编写框架的核心API的供应商。例如,不要测试向供应商的哈希表类添加项
  • 考虑使用代码覆盖率工具,如NCover,以帮助发现尚未测试的边缘案例
  • 尝试在实现之前编写测试。将测试视为实现将遵循的规范。参见行为驱动开发,测试驱动开发的一个更具体的分支

  • 保持一致。如果您只为部分代码编写测试,它几乎没有什么用处。如果您在一个团队中工作,而其他一些或所有人都不编写测试,那么这也不是很有用。让自己和其他人相信测试的重要性(以及节省时间的特性),否则就不用麻烦了

  • 你所追求的是描述被测班级的行为

  • 验证预期行为
  • 验证错误案例
  • 类中所有代码路径的覆盖率
  • 在类中执行所有成员函数
  • 基本目的是增加你对班级行为的信心

    这在重构代码时特别有用。Martin Fowler在他的网站上对测试有一个有趣的想法

    干杯


    Rob测试最初应该失败。然后,您应该编写使它们通过的代码,否则您就有可能编写一个被窃听且始终通过的测试。

    • 单元测试只是测试单元的外部API,不应该测试内部行为
    • 测试用例的每个测试都应该测试这个API中的一个(并且只有一个)方法。
      • 对于失效案例,应包括传统测试案例
    • 测试测试的覆盖率:一旦测试了一个单元,该单元内的所有行都应该被执行

    永远不要假设一个简单的两行方法可以工作。编写一个快速的单元测试是防止丢失的空测试、错放的减号和/或细微的作用域错误咬到你的唯一方法,当你处理它的时间比现在更少时,这是不可避免的。

    伟大的单元测试的一些属性:

    • 当一个测试失败时,问题所在应该立即显而易见。如果必须使用调试器来跟踪问题,那么测试的粒度不够。在这里,每个测试只有一个断言很有帮助

    • 重构时,任何测试都不应失败

    • 测试应该运行得非常快,以至于您可以毫不犹豫地运行它们

    • 所有测试应始终通过;没有不确定的结果

    • 单元测试应该被很好地考虑,就像你的产品代码一样

    @阿洛托:如果你建议一个库应该只在它的外部API上进行单元测试,我不同意。我希望对每个类进行单元测试,包括不向外部调用方公开的类。(然而,)


    编辑:有一条关于“每个测试一个断言”导致的重复的评论。具体地说,如果您有一些代码来设置场景,然后想要对其进行多个断言,但每个测试只有一个断言,那么您可以跨多个测试重复设置

    我不采取那种方法。相反,我在每个场景中使用测试夹具。下面是一个粗略的例子:

    [TestFixture]
    public class StackTests
    {
        [TestFixture]
        public class EmptyTests
        {
            Stack<int> _stack;
    
            [TestSetup]
            public void TestSetup()
            {
                _stack = new Stack<int>();
            }
    
            [TestMethod]
            [ExpectedException (typeof(Exception))]
            public void PopFails()
            {
                _stack.Pop();
            }
    
            [TestMethod]
            public void IsEmpty()
            {
                Assert(_stack.IsEmpty());
            }
        }
    
        [TestFixture]
        public class PushedOneTests
        {
            Stack<int> _stack;
    
            [TestSetup]
            public void TestSetup()
            {
                _stack = new Stack<int>();
                _stack.Push(7);
            }
    
            // Tests for one item on the stack...
        }
    }
    
    [TestFixture]
    公共类堆栈测试
    {
    [测试夹具]
    公共类空测试
    {
    堆栈(u堆栈),;
    [测试设置]
    公共void TestSetup()
    {
    _堆栈=新堆栈();
    }
    [测试方法]
    [ExpectedException(typeof(Exception))]
    公开作废
    {
    _stack.Pop();
    }
    [测试方法]
    公共空间是空的
    {
    断言(_stack.IsEmpty());
    }
    }
    [测试夹具]
    公共类pushedonetest
    {
    堆栈(u堆栈),;
    [测试设置]
    公共void TestSetup()
    {
    _堆栈=新堆栈();
    _栈推(7);
    }
    //测试堆栈上的一项。。。
    }
    }
    
    应隔离测试。一个测试不应该依赖于另一个测试。此外,测试不应依赖于外部系统。换句话说,测试您的代码,而不是您的代码所依赖的代码。您可以将这些交互作为集成或功能测试的一部分进行测试。

       - Map_DefaultConstructorShouldCreateEmptyGisMap()
       - ShouldAlwaysDelegateXMLCorrectlyToTheCustomHandlers()
       - Dog_Object_Should_Eat_Homework_Object_When_Hungry()
    
    Assert.That(x == 2 && y == 2, "An incorrect number of begin/end element 
    processing events was raised by the XElementSerializer");
    
      /// A layer cannot be constructed with a null gisLayer, as every function 
      /// in the Layer class assumes that a valid gisLayer is present.
      [Test]
      public void ShouldNotAllowConstructionWithANullGisLayer()
      {
      }