unit testingvoid方法?
unit testing一个不返回任何东西的方法的最好方法是什么? 具体在C#中。
我真正想要testing的是一种方法,它需要一个日志文件并为特定的stringparsing它。 这些string然后被插入到数据库中。 什么都没有做过,但对TDD很新,我想知道是否有可能testing这是否是真的没有得到testing。
如果一个方法不返回任何东西,它可以是下面的一个
- 命令性的 – 你要么要求对象自己做一些事情,例如改变状态(不要期待任何证实……假定它将被完成)
- 信息 – 只是通知某人事情发生了(不期待行动或回应)分别。
命令式的方法 – 你可以validation任务是否被实际执行。 validation状态改变是否发生。 例如
void DeductFromBalance( dAmount )
可以通过validation发送此消息的余额是否确实小于dAmount的初始值来进行testing
信息方法 – 作为对象的公共接口的成员是罕见的…因此通常不进行unit testing。 但是,如果必须的话,您可以validation是否需要处理通知。 例如
void OnAccountDebit( dAmount ) // emails account holder with info
可以通过validation电子邮件是否正在发送进行testing
发布更多关于你的实际方法的细节,人们将能够更好地回答。
更新 :你的方法正在做2件事情。 我实际上将它分成两个方法,现在可以独立testing。
string[] ExamineLogFileForX( string sFileName ); void InsertStringsIntoDatabase( string[] );
string[]可以很容易通过提供第一个方法来validation一个虚拟文件和期望的string。 第二个是有点棘手..你可以使用Mock(模仿框架谷歌或searchstackoverflow)模仿数据库或击中实际的数据库,并validation是否string插入正确的位置。 检查这个线程的一些好书…如果你处于紧缩状态,我会推荐实用的unit testing。
在代码中,它会像使用
InsertStringsIntoDatabase( ExamineLogFileForX( "c:\OMG.log" ) );
testing其副作用。 这包括:
- 它是否会抛出任何exception? (如果应该的话,检查一下,如果不是的话,可以尝试一些可能的情况,如果你不小心的话 – 空论是最明显的。
- 它的参数很好吗? (如果它们是可变的,它不应该变异,反之亦然?)
- 它是否对您所调用的对象/types的状态有正确的影响?
当然,你可以testing的数量是有限的。 例如,您通常不能testing每个可能的input。 以务实的方式进行testing – 足以让您相信您的代码devise得当,并且正确实施,并且足以作为调用者期望的补充文档。
一如往常:testing方法应该做什么!
它应该改变全球的状态(呃,代码味道!)在某个地方?
它应该调用一个接口?
它应该抛出一个exception时调用错误的参数?
使用正确的参数调用时是否会引发exception?
应该是 …?
无效返回types/子程序是旧消息。 在8年的时间里,我还没有做出一个Void返回types(除非我是非常懒惰的)(从这个答案的时间开始,所以在这个问题之前就这么问了)。
而不是像这样的方法:
public void SendEmailToCustomer()
制作一个遵循微软的int.TryParse()范例的方法:
public bool TrySendEmailToCustomer()
也许从长远来看,你的方法没有任何需要返回使用的信息,但是在执行它的工作之后返回方法的状态对于调用者来说是一个巨大的用处。
而且,bool不是唯一的状态types。 有一些以前的子程序可以返回三个或更多的不同状态(好,正常,坏等)。 在这些情况下,你只需要使用
public StateEnum TrySendEmailToCustomer()
然而,尽pipeTry-Paradigm在某种程度上回答了如何testing无效回报的问题,但还有其他一些考虑因素。 例如,在“TDD”循环期间/之后,您将会“重构”,并注意到您正在使用您的方法执行两件事情,从而打破了“单一责任原则”。 所以应该首先照顾。 其次,您可能已经识别出一个依赖关系…您正在触摸“持久”数据。
如果您正在使用问题方法中的数据访问方式,则需要重构为n层或n层架构。 但是我们可以假设,当你说“string被插入到数据库中”的时候,你实际上是指你正在调用一个业务逻辑层或者什么东西。 雅,我们会假设的。
当你的对象被实例化时,你现在明白你的对象有依赖关系。 这是当你需要决定是否要在对象或方法上进行dependency injection的时候。 这意味着您的构造函数或问题方法需要一个新的参数:
public <Constructor/MethodName> (IBusinessDataEtc otherLayerOrTierObject, string[] stuffToInsert)
现在您可以接受业务/数据层对象的接口了,您可以在unit testing期间将其模拟出来,并且不存在依赖性或担心“意外”集成testing。
因此,在您的实时代码中,传入一个REAL IBusinessDataEtc
对象。 但在你的unit testing中,你传入一个MOCK IBusinessDataEtc
对象。 在这个模拟中,你可以包括像int XMethodWasCalledCount
这样的非接口属性,或者当接口方法被调用时其状态被更新的东西。
因此,您的unit testing将通过您的方法 – 问题,执行任何逻辑,并在您的IBusinessDataEtc
对象中调用一个或两个方法或一组选定的方法。 当你在你的unit testing结束时进行断言时,你现在有几件事要testing。
- “子程序”现在是一种尝试范式方法。
- 您的模拟
IBusinessDataEtc
对象的状态。
有关构造级别的dependency injection思想的更多信息…因为它们与unit testing有关…查看Builderdevise模式。 它为每个当前的接口/类增加了一个接口和类,但是它们非常小,并提供巨大的function增加,以便更好地进行unit testing。
它会对一个对象产生一些影响….查询效果的结果。 如果没有可见的效果,那么不值得unit testing!
推测该方法做了一些事情,而不是简单地返回?
假设情况如此,那么:
- 如果它修改它的所有者对象的状态,那么你应该testing状态正确改变。
- 如果它将某个对象作为参数并修改该对象,则应该testing该对象是否被正确修改。
- 如果在某些情况下抛出exception,请testing这些exception是否正确抛出。
- 如果其行为根据自己的对象或其他对象的状态而变化,则预置状态并通过上述三种testing方法之一来testing方法是否具有正确的I)。
如果你让我们知道这个方法做什么,我可以更具体。
使用Rhino Mocks来设置可能的调用,操作和exception。 假设你可以模拟或者删除你的方法的一部分。 很难知道这里不知道一些关于方法,甚至上下文的细节。
取决于它在做什么。 如果它有参数,则传入一些你可能会问的问题,如果他们被调用了正确的参数。
尝试这个:
[TestMethod] public void TestSomething() { try { YourMethodCall(); Assert.IsTrue(true); } catch { Assert.IsTrue(false); } }
您还应该检查方法是否引发exception。
你用什么来调用void方法,你可以使用, Verfiy
例如:
在我的情况下,它的_Log
是实例, LogMessage
是要testing的方法:
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); }
由于testing失败的方法失败, Verify
引发exception?