非常简单的方法的单元测试(是或否)

时间:2009-08-03 10:16:32

标签: unit-testing nunit moq

假设你有一个方法:

public void Save(Entity data)
{
    this.repositoryIocInstance.EntitySave(data);
}

你会写一个单元测试吗?

public void TestSave()
{
    // arrange
    Mock<EntityRepository> repo = new Mock<EntityRepository>();
    repo.Setup(m => m.EntitySave(It.IsAny<Entity>());

    // act
    MyClass c = new MyClass(repo.Object);
    c.Save(new Entity());

    // assert
    repo.Verify(m => EntitySave(It.IsAny<Entity>()), Times.Once());
}

因为稍后如果你改变方法的实现来做更多“复杂”的事情,比如:

public void Save(Entity data)
{
    if (this.repositoryIocInstance.Exists(data))
    {
        this.repositoryIocInstance.Update(data);
    }
    else
    {
        this.repositoryIocInstance.Create(data);
    }
}

...你的单元测试会失败,但它可能不会破坏你的应用......

问题

我是否应该打扰在没有任何返回类型*或**不改变内部模拟之外的任何方法的方法上创建单元测试?

8 个答案:

答案 0 :(得分:7)

不要忘记单元测试不只是测试代码。它是关于允许您确定行为更改时的

所以你可能会有一些微不足道的东西。但是,您的实施会发生变化,您可能会产生副作用。您希望回归测试套件告诉您。

e.g。通常人们会说你不应该测试二传手/吸气剂,因为它们是微不足道的。我不同意,不是因为它们是复杂的方法,而是有人可能无意中通过无知,胖手指等方式改变它们。

考虑到我刚刚说过的所有内容,我肯定会对上面的内容进行测试(通过模拟,和/或可能值得设计具有可测试性的类并让它们报告状态等等。)

答案 1 :(得分:3)

这是真的,你的测试取决于你的实现,这是你应该避免的事情(尽管有时并不是那么简单......)并且不一定是坏事。 但即使您的更改没有破坏代码,这些测试也会被打破。

你可以有很多方法:

  • 创建一个真正进入数据库的测试,并检查状态是否按预期更改(不会再次成为单元测试)
  • 创建一个伪造数据库并在内存中执行操作的测试对象(repositoryIocInstance的另一个实现),并验证状态是否按预期更改。对存储库接口的更改也会对此对象的更改产生影响。但是你的界面不应该有太大变化,对吗?
  • 看到所有这些都太贵了,并且使用你的方法,这可能会导致以后不必要地破坏测试(但是一旦机会很低,就可以承担风险)

答案 2 :(得分:3)

问自己两个问题。 &#34;本单元测试的手册相当于什么?&#34;和#34;是否值得自动化?&#34;。在你的情况下,它将是这样的:

什么是手动等效?   - 启动调试器   - 进入&#34; Save&#34;方法   - 进入下一步,确保您已进入IRepository.EntitySave实施

值得自动化吗?我的回答是&#34;没有&#34;。从代码中可以看出100%。 从数百次类似的废物测试中,我没有看到一个有用的东西。

答案 3 :(得分:2)

一般的经验法则是,你测试所有可能会破坏的东西。如果您确定,该方法足够简单(并且保持足够简单)以避免出现问题,那么就可以通过测试来解决问题。

第二件事是,你应该测试方法的合同,而不是实现。如果测试在更改后失败,而不是应用程序,那么您的测试测试不是正确的。测试应涵盖对您的应用很重要的案例。这应该确保对方法的每次更改都不会破坏应用程序。

答案 4 :(得分:2)

您问题的简短回答是:,您绝对应该测试类似的方法。

我认为Save方法重要实际上保存数据。如果您没有为此编写单元测试,那么您怎么知道?

其他人可能会出现并删除调用EntitySave方法的代码行,并且所有单元测试都不会失败。后来,你想知道为什么物品永远不会存在......

在你的方法中,你可以说任何删除该行的人只有在有恶意的情况下才会这样做,但问题是:简单的事情并不一定保持简单,你最好在事情发生之前编写单元测试复杂。

不是一个实现细节,Save方法在Repository上调用EntitySave - 它是预期行为的一部分,如果我可以这么说,它是一个非常重要的部分。您希望确保实际保存数据。

仅仅因为方法没有返回值并不意味着它不值得测试。通常,如果您观察到良好的命令/查询分离(CQS),那么任何void方法都应该改变某事的状态。

有时候某些东西是阶级本身,但有时候,它可能是其他东西的状态。在这种情况下,它会更改存储库的状态,这就是您应该测试的内容。

这称为测试不正确的输出,而不是更正常的直接输出(返回值)。

诀窍是编写单元测试,以便它们不会经常中断。使用Mocks时,很容易意外地编写 Overspecified Tests ,这就是为什么大多数动态模拟(如Moq)默认为 Stub 模式的原因,其中并不重要很多时候你会调用给定的方法。

所有这些以及更多内容都在优秀的xUnit Test Patterns中解释。

答案 5 :(得分:1)

不返回任何结果的方法仍会更改应用程序的状态。在这种情况下,您的单元测试应该测试新状态是否符合预期。

答案 6 :(得分:1)

“您的单元测试会失败,但可能不会破坏您的应用程序”

这实际上是非常重要的。它可能看起来很烦人而且微不足道,但是当别人开始维护你的代码时,他们可能会对Save和(不可能)破坏应用程序做出非常糟糕的改变。

诀窍是优先考虑。

首先测试重要的东西。当事情进展缓慢时,为琐碎的东西添加测试。

答案 7 :(得分:1)

当方法中没有断言时,你基本上断言不会抛出异常。

我也在努力解决如何测试public void myMethod()的问题。我想如果您决定为可测试性添加返回值,则返回值应该代表查看应用程序状态发生了哪些变化所需的所有重要事实。

public void myMethod()

变为

 public ComplexObject myMethod() { 
 DoLotsOfSideEffects()
 return new ComplexObject { rows changed, primary key, value of each column, etc };
 }

而不是

public bool myMethod()  
  DoLotsOfSideEffects()
  return true;