对一个不返回任何东西的方法进行单元测试的最佳方法是什么?特别是在c#中。

我真正想测试的是一个方法,它接受一个日志文件并解析它的特定字符串。然后将字符串插入到数据库中。没有什么是以前没有做过的,但对于TDD来说是非常新的,我想知道是否有可能测试这个,或者它是没有真正测试过的东西。


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


这取决于它在做什么。如果它有参数,则传入模拟,以便稍后询问是否使用正确的参数集调用了它们。


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

它是否应该在某个地方改变全局状态(哎呀,代码味道!)?

它是否应该调用接口?

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

当使用正确的参数调用时,它是否不抛出异常?

应该……吗?


假设方法做了一些事情,而不是简单地返回?

假设是这样,那么:

If it modifies the state of it's owner object, then you should test that the state changed correctly. If it takes in some object as a parameter and modifies that object, then your should test the object is correctly modified. If it throws exceptions is certain cases, test that those exceptions are correctly thrown. If its behaviour varies based on the state of its own object, or some other object, preset the state and test the method has the correct Ithrough one of the three test methods above).

如果你告诉我们这个方法是做什么的,我可以更具体地说。


如果一个方法没有返回任何东西,它是下列情况之一

imperative -你要么要求对象对自己做一些事情。例如改变状态(不期待任何确认..这是假定会完成的) 信息-只是通知某人某事发生了(不期待行动或回应)。

命令式方法—您可以验证任务是否实际执行。验证状态是否发生了变化。如。

void DeductFromBalance( dAmount ) 

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

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

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

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

发布更多关于你实际方法的细节,人们就能更好地回答问题。 更新:你的方法是做两件事。我实际上把它分成了两个方法,现在可以独立测试了。

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

String[]可以通过向第一个方法提供一个虚拟文件和期望的字符串轻松验证。第二个问题有点棘手。您可以使用Mock(谷歌或在Mock框架上搜索stackoverflow)来模拟DB或击中实际的DB并验证字符串是否插入到正确的位置。检查这个线程的一些好书…如果你手头紧,我建议你使用实用单元测试。 在代码中,它的用法是

InsertStringsIntoDatabase( ExamineLogFileForX( "c:\OMG.log" ) );

测试它的副作用。这包括:

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

当然,您可以测试的内容是有限的。例如,您通常不能对每个可能的输入进行测试。实用的测试——足够让你相信你的代码设计得当,实现正确,并且足够作为调用者可能期望的补充文档。


使用Rhino mock来设置可能预期的调用、操作和异常。假设您可以模拟或去掉方法的某些部分。如果不了解关于方法甚至上下文的一些细节,就很难知道。


Void返回类型/子例程已经过时了。我没有做一个Void返回类型(除非我是非常懒惰)在像8年(从这个答案的时间,所以只是在这个问题被问之前的一点)。

而不是像这样的方法:

public void SendEmailToCustomer()

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

public bool TrySendEmailToCustomer()

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

而且,bool并不是唯一的状态类型。有很多次,以前制作的子例程实际上可以返回三种或更多不同的状态(好,正常,坏,等等)。在这种情况下,你只需使用

public StateEnum TrySendEmailToCustomer()

然而,尽管Try-Paradigm在某种程度上回答了如何测试void返回的问题,但也有其他考虑因素。例如,在“TDD”周期中/之后,你会“重构”,并注意到你用你的方法做了两件事……从而打破了“单一责任原则”。所以首先要解决这个问题。其次,您可能已经确定了一个依赖项……您正在接触“持久”数据。

如果您正在使用所讨论的方法进行数据访问,则需要将其重构为n层或n层体系结构。但我们可以假设,当您说“字符串随后被插入到数据库中”时,您实际上是在调用业务逻辑层或其他东西。是的,我们假设是这样。

当对象实例化后,您现在就知道对象具有依赖关系了。这时你需要决定是在对象上还是在方法上进行依赖注入。这意味着你的构造函数或方法需要一个新的参数:

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

现在您可以接受业务/数据层对象的接口,您可以在单元测试期间模拟它,无需依赖或担心“意外”集成测试。

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

因此,您的单元测试将遍历您的Method(s)-In-Question,执行它们所具有的任何逻辑,并调用IBusinessDataEtc对象中的一个或两个或一组选定的方法。当你在单元测试的末尾执行断言时,你现在有两个东西要测试。

“子例程”的状态,现在是一个尝试范式方法。 模拟IBusinessDataEtc对象的状态。

有关构造层依赖注入思想的更多信息…因为它们与单元测试有关……查看Builder设计模式。它为当前的每个接口/类增加了一个接口和类,但它们非常小,为更好的单元测试提供了巨大的功能增加。


试试这个:

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

无论你使用什么实例来调用void方法,你可以使用,验证

例如:

在我的例子中,它的_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);
}

验证是否由于测试将失败的方法失败而抛出异常?


你甚至可以这样尝试:

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