gpt4 book ai didi

c# - 了解一些单元测试实践

转载 作者:行者123 更新时间:2023-12-02 22:40:12 24 4
gpt4 key购买 nike

我是单元测试的新手 - 我只使用 Testmethods(我的最后一个模块,我创建了其中的大约 50 个)完成了基本的断言测试。

我目前正在阅读一本关于单元测试的书,书中有许多示例让我为每个测试创建一个新类。下面是仅为一个测试用例创建的示例对象之一。我的问题是是否有必要这样做?或者什么时候应该应用这种方法,什么时候不需要?

  public class and_saving_an_invalid_item_type : when_working_with_the_item_type_repository
{
private Exception _result;

protected override void Establish_context()
{
base.Establish_context();

_session.Setup(s => s.Save(null)).Throws(new ArgumentNullException());
}

protected override void Because_of()
{
try
{
_itemTypeRepository.Save(null);
}
catch (Exception exception)
{
_result = exception;
}
}

[Test]
public void then_an_argument_null_exception_should_be_raised()
{
_result.ShouldBeInstanceOfType(typeof(ArgumentNullException));
}
}

最佳答案

您是否需要为每个单独的测试创建一个新类?我会说不,你当然不会。我不知道这本书为什么这么说,或者他们这样做只是为了帮助说明他们的例子。

为了回答您的问题,我建议为每个 测试使用一个类...但它确实比这复杂一点,因为您定义“组”的方式各不相同并且取决于你当时在做什么。

根据我的经验,一组测试在逻辑上确实像文档一样结构化,其中可以包含一组或多组测试,按某些共同方面分组(有时嵌套)在一起。测试面向对象代码的自然分组是按类分组,然后按方法分组。

举个例子

  • 1 类测试
    • 测试方法 1
      • 方法 1 的主要行为
      • 方法 1 的替代行为
    • 测试方法 2
      • 方法 2 的主要行为
      • 方法 2 的替代行为

不幸的是,在 C# 或 Java(或类似语言)中,您只能使用两层结构(而不​​是您真正想要的 3 或 4 层),因此您必须破解一些东西以适应.

完成此操作的常见方法是使用一个类将测试集组合在一起,并且不要在方法级别对任何内容进行分组,如下所示:

class TestsForClass1 {
void Test_method1_primary()
void Test_method1_alternate()

void Test_method2_primary()
void Test_method2_alternate()
}

如果您的方法 1 和方法 2 都具有相同的设置/拆卸,那么这很好,但有时它们不会,导致这种故障:

class TestsForClass1_method1 {
void Test_primary()
void Test_alternate()
}

class TestsForClass1_method2 {
void Test_primary()
void Test_alternate()
}

如果您有更复杂的需求(假设您对 method_1 进行了 10 次测试,前 5 次有设置要求 X,接下来的 5 次有不同的设置要求),那么人们通常最终只会像这样创建越来越多的类名:

class TestsForClass1_method1_withRequirementX {  ... }
class TestsForClass1_method1_withRequirementY { ... }

这很糟糕,但是嘿 - 方钉、圆孔等。

就个人而言,我喜欢在方法内部使用 lambda 函数来为您提供第三级分组。 NSpec展示了一种可以做到这一点的方法......我们有一个内部测试框架,它略有不同,它看起来有点像这样:

class TestsForClass1 {
void TestsForMethod1() {
It.Should("perform it's primary function", () => {
// ....
});

It.Should("perform it's alternate function", () => {
// ....
});
}
}

这有一些缺点(如果第一个 It 语句失败,其他的就不会运行),但我认为这种权衡是值得的。)


-- 最初的问题是:“是否真的有必要为我要执行的每个测试创建一个对象?”。根据这个解释,答案(大部分)是肯定的。

一般来说,单元测试涉及两部分的交互

  • 被测对象。通常这是您编写的类或函数的实例
  • 环境。通常这是您传递给函数的任何参数,以及对象可能引用的任何其他依赖项。

为了使单元测试可靠,这两个部分对于每次测试都需要“新鲜”,以确保系统的状态是健全和可靠的。

  • 如果每次测试都没有刷新被测对象,那么一个函数可能会改变对象的内部状态,并导致下一次测试错误地失败

  • 如果没有为每个测试刷新环境,那么一个函数可能会改变环境(例如:在外部数据库中设置一些变量或其他东西),这可能会导致下一个测试错误地失败。

显然有很多情况并非如此 - 例如,您可能有一个纯数学函数,它只将整数作为参数并且不触及任何外部状态,然后您可能不想费心重新创建被测对象或测试环境……但一般来说,任何面向对象系统中的大多数东西都需要刷新,所以这就是为什么这样做是“标准做法”。

关于c# - 了解一些单元测试实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10889665/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com