如何使用一个界面测试不同的类?

时间:2011-11-05 18:04:00

标签: c# interface nunit

我有一些界面ISome。有4个类,如MyClass1:ISome,MyClass2:ISome等。 如何使用NUnit从这个接口测试一些方法,但是对所有类只进行1次单元测试?

5 个答案:

答案 0 :(得分:4)

4个类意味着给定方法的4种不同实现。因此,您应该进行4次单元测试。尝试编写单个单元测试是错误的。

答案 1 :(得分:3)

  

如何使用NUnit从此接口测试某个方法,但是对所有类只进行1次单元测试?

你写这篇文章的方式,人们可以把它解释为“单个单元测试,时期”。这当然是一个非常糟糕的主意。

理想情况下,您希望每个测试用例只能使一个断言,并且您确实必须在代码中断言所有不同的逻辑部分。每个类都有多个逻辑,接口的每个实现都是一个截然不同的逻辑(即使它有相同的接口,即使你复制和粘贴代码)。

所以你面临一个选择:你需要多个单元测试

代码重复

避免重复单元测试代码,您可以尝试NUnit的参数化单元测试功能。

有了它,您可以将实际编写的单元测试方法的数量乘以您提供给这些测试的实例数。这些实例可能是不同的实现。

在NUnit中有很多方法可以参数化你的测试。看到这篇文章:

我个人更喜欢TestCaseSource属性:

NUnit测试运行器仍将显示您编写的每个单元测试方法,但会将数据阵列中的每个项目显示为子测试。

学习如何使用这些是很棘手的,因为你必须将测试分解为数据驱动的模型。

如何参与实施

如果你的界面中给定方法的实现只是吐出不同的数据,那么你可能会很幸运。例如。如果你在一个实现中测试乘法,在另一个方法中测试加法,你的矩阵可能如下所示:

Impl      Input1  Input2  Result
Multiply  0       7       0
Multiply  3       6       18
Add       0       0       0
Add       5       6       11

您将返回一个数组,该数组包含此矩阵中的每一行,包括您正在运行的接口的哪个实现,将它们提供给体现测试一般流程的单个单元测试方法,并将其调用完成。

以下是一些实现这些操作的示例代码:

// This interface takes two ints, returns one int
public interface IBinaryOperation
{
    int Execute(int x, int y);
    string Name { get; }
}

public class Add : IBinaryOperation
{
    public int Execute(int x, int y) { return x + y; }
    public string Name { get { return "Add"; } }
}

public class Multiply : IBinaryOperation
{
    public int Execute(int x, int y) { return x * y; }
    public string Name { get { return "Multiply"; } }
}

这是一个示例测试夹具实现:

[Test, TestCaseSource("OperationTestCases")]
public void ExecuteReturnsCorrectResult(
    IBinaryOperation operation,
    int inputX, int inputY,
    int expectedResult
    )
{
    int actualResult = operation.Execute(inputX, inputY);
    Assert.That(actualResult, Is.EqualTo(expectedResult),
        "Expected operation: '{0}', with '{1}' and '{2}' to return '{3}'"
        , operation.Name
        , inputX
        , inputY
        , expectedResult
        );
}

static object[] OperationTestCases =
{
    new object[] { new Multiply(), 0, 7, 0 },
    new object[] { new Multiply(), 3, 6, 18 },
    new object[] { new Add(), 0, 0, 0 },
    new object[] { new Add(), 5, 6, 11 },
};

Results from running this test suite in NUnit

更复杂的例子

如果您的实现彼此之间的差异更大,或者您无法创建简单的输入/输出矩阵,那么您将面临更难的时间。

一种选择是将代表提供给您的测试用例。但这听起来很复杂,不清楚,难以维护。在这种情况下,您最好更好地编写单独的单元测试方法。

答案 2 :(得分:2)

Darin Dimitrov一般是正确的,但为方便起见,您可以为所有实现实现一个抽象测试用例。有一个像

这样的抽象方法
protected abstract ISome createInstance();

并在继承自抽象测试类的测试用例中覆盖它将起作用。

答案 3 :(得分:1)

你可以查看Greg Young的界面不变NUnit插件:https://github.com/gregoryyoung/grensesnitt

答案 4 :(得分:0)

您不应该将您创建的单元测试数与代码中的类数相关联。理想情况下,您希望至少覆盖尽可能多的代码路径,以及您可以想到的许多不同场景。启发式通常应该为每个级别提供多个单元测试。在你的情况下,你应该至少 4,但是你必须编写更多(对于非平凡的代码)来真正运用有意义的代码方式。