Unit testing 保持单元测试的独立性和独立性

Unit testing 保持单元测试的独立性和独立性,unit-testing,Unit Testing,我很难让我的单元测试彼此独立。例如,我有一个链表,它有两个append方法,一个方法获取单个元素并将其附加到列表中,另一个方法获取另一个列表并将整个元素附加到列表中;但是如果不使用第一个append方法来填充我传递的列表,我就无法测试第二个append方法(获取整个列表的方法)。如何使这两种方法的单元测试彼此独立?您可以创建一种方法,该方法本身不是单元测试方法,而是为多个测试创建条件,然后执行结果验证。您实际的单元测试方法将调用此其他方法。因此,您可以在多个测试中使用相同的数据集,而不会在测试方

我很难让我的单元测试彼此独立。例如,我有一个链表,它有两个
append
方法,一个方法获取单个元素并将其附加到列表中,另一个方法获取另一个列表并将整个元素附加到列表中;但是如果不使用第一个
append
方法来填充我传递的列表,我就无法测试第二个
append
方法(获取整个列表的方法)。如何使这两种方法的单元测试彼此独立?

您可以创建一种方法,该方法本身不是单元测试方法,而是为多个测试创建条件,然后执行结果验证。您实际的单元测试方法将调用此其他方法。因此,您可以在多个测试中使用相同的数据集,而不会在测试方法之间引入依赖关系

我不知道您使用的是哪种语言,但这里有一个例子是Xcode 5中的Objective-C和新的XCTest框架。我会这样做:

- (void)performTestWithArray:(NSArray *)list
{
    NSMutableArray *initialList = ...; // create the initial list you will use with multiple tests
    [initialList addObjectsFromArray:list];
    XCTAssertTrue(testCondition, @"message");
}

- (void)testAddSingleElement
{
    NSArray *array = @[ @"one element" ];
    [self performTestWithArray:array];
}

- (void)testAddList
{
    NSArray *array = @[ @"first element", @"second element", @"third element" ];
    [self performTestWithArray:array];
}

您描述的情况在测试中随处可见:您有一些类或库要测试。类或库具有某些需要测试的方法/函数,对于其中一些方法/函数的测试,您必须调用该库的其他方法/函数

换句话说,当根据四阶段测试模式(设置、练习、评估、清理)分解测试时,您希望在练习阶段调用类/库。但是,在设置阶段,也可能在评估和/或清理阶段,必须调用其中的某些元素,这似乎很烦人

这是不可避免的:您提到在list
append
函数的设置中,必须使用单个元素
append
函数。但是,更糟糕的是:您还必须使用list类的构造函数——没有这个构造函数就没有机会逃脱。但是,构造函数也可能有bug

可以肯定的是,测试失败(或者错误地通过)是因为设置中调用的函数有缺陷。但是,一个合适的测试套件(正如注释中提到的)还应该对其他(称为较低级别)功能进行测试

例如,您应该有许多测试来检查类的构造函数是否正常工作。如果在某个时候修改了构造函数,使其出现缺陷,则在设置阶段使用该构造函数的所有测试都不再可信。但是,一些测试构造函数本身(因此在练习阶段调用它)的测试现在应该失败了

从测试结果的概述中,您将能够确定测试失败的根本原因。这需要对依赖关系有一些了解:哪些测试关注较低级别的方面,哪些测试更高级别,因为它们依赖于较低级别的功能来工作

有一些方法可以使这些依赖关系更加明显,从而使以后更容易分析测试失败-但这些都不是必需的:

  • 在测试代码中,您将较低级别方面的测试放在文件的顶部,而更相关的测试则放在底部。然后,当几个测试失败时,首先查看最靠近文件顶部的测试。请注意,测试代码中的测试顺序并不一定意味着执行顺序:例如,JUnit并不关心测试方法在测试类中的编写顺序
  • 正如注释中所建议的,您还可以配置测试框架,以便在其他测试之前运行较低级别的测试

为什么需要将它们以这种方式分开?一般来说,单元测试应该是独立的,因为你可以按任何顺序运行它们并得到相同的结果。我认为它们也应该是独立的,因为它们应该只测试一件事情。如果我在每个测试中测试两个或两个以上的东西,那么很难知道哪一个失败了。我发现先运行较低级别的测试很有帮助。首先测试底层操作(那些不太依赖于其他操作的操作),这样您就知道底层操作是有效的,然后运行可能在流程中使用这些底层操作的高级测试。这样一来,较早失败的测试更有可能出现真正的问题。我敢肯定你只是自相矛盾。首先你说一个人的单元测试应该是顺序独立的,然后你说你应该首先运行你的低级测试。我在Java中使用jUnit,jUnit不允许对测试进行排序。我不能保证一个测试在另一个测试之前运行。区别在于是否可以以不同的顺序运行它们,而不是实际运行它们的顺序。重要的是,可以以不同的顺序运行它们,以便您可以根据需要自由更改顺序。如果需要按照特定的顺序运行测试才能正常工作,那么很容易在测试中引入细微的错误。将测试从较低的级别排序到较高的级别只是为了方便在测试代码中查找bug。在
testAddSingleElement
myArray
来自哪里?
performTestWithArray
是从哪里来的?哎呀,我不完全地重命名了一些东西。我更新了我的示例。