单元测试无效方法?

时间:2008-10-29 07:28:13

标签: c# unit-testing

对不返回任何内容的方法进行单元测试的最佳方法是什么?特别是在c#。

我真正想要测试的是一种获取日志文件并针对特定字符串进行解析的方法。然后将字符串插入数据库。没有什么比以前没有做过但对TDD来说是非常新的我想知道是否有可能测试这个或者它是否真的没有经过测试。

11 个答案:

答案 0 :(得分:135)

如果方法没有返回任何内容,则可以是以下任何一种

  • 强制性 - 你要么要求对象对自己做一些事情......例如改变状态(不要期待任何确认......它假定它会被完成)
  • 信息 - 只是分别通知某人发生了某些事情(没有预料到行动或回应)。

命令式方法 - 您可以验证任务是否实际执行。验证是否实际发生了状态更改。 e.g。

void DeductFromBalance( dAmount ) 

可以通过验证此消息的余额是否确实小于dAmount的初始值来测试

信息方法 - 很少作为对象的公共接口的成员...因此通常不进行单元测试。但是,如果必须,您可以验证是否要对通知进行处理。 e.g。

void OnAccountDebit( dAmount )  // emails account holder with info

可以通过验证电子邮件是否正在发送进行测试

发布有关您的实际方法的更多详细信息,人们将能够更好地回答。
更新:您的方法正在做两件事。我实际上将它分成两种方法,现在可以独立测试。

string[] ExamineLogFileForX( string sFileName );
void InsertStringsIntoDatabase( string[] );

通过为第一个方法提供虚拟文件和预期的字符串,可以轻松验证String []。第二个有点棘手..你可以使用Mock(google或在模拟框架上搜索stackoverflow)来模仿数据库或点击实际数据库并验证字符串是否插入到正确的位置。检查this thread是否有一些好书...如果你处于紧张状态,我会建议实用单位测试。
在代码中,它将像

一样使用
InsertStringsIntoDatabase( ExamineLogFileForX( "c:\OMG.log" ) );

答案 1 :(得分:55)

测试其副作用。这包括:

  • 是否会抛出任何异常? (如果它应该,检查它是否。如果它不应该,尝试一些极端情况,如果你不小心可能 - 空参数是最明显的事情。)
  • 它的参数能很好地发挥吗? (如果它们是可变的,它会在不应该变异的情况下变异,反之亦然吗?)
  • 它对您调用它的对象/类型的状态是否有正确的影响?

当然,你可以测试的很多是有限的。例如,您通常无法测试每个可能的输入。实用性测试 - 足以让您确信您的代码设计得恰当并且正确实现,并且足以充当调用者可能期望的补充文档。

答案 2 :(得分:27)

一如既往:测试该方法应该做什么!

它应该在某处改变全局状态(uuh,代码味道!)吗?

它应该调用接口吗?

使用错误的参数调用时是否会抛出异常?

使用正确的参数调用时是否应该抛出异常?

它应该......?

答案 3 :(得分:9)

Void返回类型/子例程是旧消息。在8年的时间里,我还没有做出Void返回类型(除非我非常懒惰)(从这个答案的时候起,所以在问这个问题之前就已经有点了。)

而不是类似的方法:

public void SendEmailToCustomer()

制作一个遵循微软的int.TryParse()范例的方法:

public bool TrySendEmailToCustomer()

也许没有任何信息需要您的方法返回以供长期使用,但在执行其工作后返回方法的状态对调用者来说是一个巨大的用途。

另外,bool不是唯一的州类型。有许多次,以前制作的子程序实际上可以返回三种或更多种不同的状态(Good,Normal,Bad等)。在这些情况下,您只需使用

public StateEnum TrySendEmailToCustomer()

然而,虽然Try-Paradigm在某种程度上回答了关于如何测试void返回的问题,但还有其他考虑因素。例如,在" TDD"期间/之后循环,你会"重构"并注意到你正在用你的方法做两件事......从而违反了单一责任原则。"所以应该先照顾好。其次,你可能已经认识到一种依赖...你正在触摸"持续"数据

如果您正在使用相关方法中的数据访问内容,则需要重构为n层或n层体系结构。但我们可以假设,当你说"然后将字符串插入到数据库中时,你实际上意味着你正在调用业务逻辑层或其他东西。雅,我们假设。

当您的对象被实例化时,您现在知道您的对象具有依赖性。这是当您需要决定是否要对对象或方法执行依赖注入时。这意味着您的构造函数或有问题的方法需要一个新参数:

public <Constructor/MethodName> (IBusinessDataEtc otherLayerOrTierObject, string[] stuffToInsert)

现在您可以接受业务/数据层对象的接口,您可以在单元测试期间将其模拟出来,并且没有依赖性或担心&#34;意外&#34;集成测试。

因此,在您的实时代码中,您传入一个REAL IBusinessDataEtc对象。但是在单元测试中,您传入一个MOCK IBusinessDataEtc对象。在那个Mock中,你可以包括非接口属性,如int XMethodWasCalledCount或者在调用接口方法时更新状态的东西。

因此,您的单元测试将通过您的方法-In-Question,执行他们拥有的任何逻辑,并在IBusinessDataEtc对象中调用一个或两个或一组选定的方法。当您在单元测试结束时进行断言时,您现在需要测试几件事。

  1. &#34;子程序&#34;的状态现在这是一种Try-Paradigm方法。
  2. 模拟IBusinessDataEtc对象的状态。
  3. 有关构建级别的依赖注入思想的更多信息......因为它们与单元测试有关...请查看构思器设计模式。它为您拥有的每个当前接口/类增加了一个接口和类,但它们非常小,并且为更好的单元测试提供了巨大的功能增加。

答案 4 :(得分:5)

它会对对象产生一些影响....查询效果的结果。如果它没有明显效果,那就不值得单元测试了!

答案 5 :(得分:4)

大概这个方法做了什么,而不仅仅是回归?

假设是这种情况,那么:

  1. 如果它修改了它的所有者对象的状态,那么你应该测试状态是否正确改变了。
  2. 如果它将某个对象作为参数并修改该对象,那么您应该测试该对象是否被正确修改。
  3. 如果在特定情况下抛出异常,请测试是否正确抛出了这些异常。
  4. 如果其行为因其自身对象或某个其他对象的状态而异,则预设状态并测试该方法是否具有上述三种测试方法中的正确I通道之一。)
  5. 如果你让我们知道方法的作用,我可以更具体一点。

答案 6 :(得分:4)

试试这个:

[TestMethod]
public void TestSomething()
{
    try
    {
        YourMethodCall();
        Assert.IsTrue(true);
    }
    catch {
        Assert.IsTrue(false);
    }
}

答案 7 :(得分:3)

使用Rhino Mocks设置可能需要的呼叫,操作和例外。假设您可以模拟或删除方法的一部分。很难知道这里有关于方法甚至上下文的一些具体细节。

答案 8 :(得分:2)

取决于它在做什么。如果它有参数,那么传入你稍后可能会询问的模拟是否已使用正确的参数集调用它们。

答案 9 :(得分:0)

您使用什么实例来调用void方法,您可以使用Verfiy

例如:

在我的情况下,_Log是实例,LogMessage是要测试的方法:

try
{
    this._log.Verify(x => x.LogMessage(Logger.WillisLogLevel.Info, Logger.WillisLogger.Usage, "Created the Student with name as"), "Failure");
}
Catch 
{
    Assert.IsFalse(ex is Moq.MockException);
}

由于方法失败,Verify是否会引发异常,测试会失败吗?

答案 10 :(得分:0)

您甚至可以这样尝试:

[TestMethod]
public void ReadFiles()
{
    try
    {
        Read();
        return; // indicates success
    }
    catch (Exception ex)
    {
        Assert.Fail(ex.Message);
    }
}