单元测试无效方法?

jdi*_*iaz 158 c# unit-testing

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

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

Gis*_*shu 141

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

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

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

void DeductFromBalance( dAmount ) 
Run Code Online (Sandbox Code Playgroud)

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

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

void OnAccountDebit( dAmount )  // emails account holder with info
Run Code Online (Sandbox Code Playgroud)

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

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

string[] ExamineLogFileForX( string sFileName );
void InsertStringsIntoDatabase( string[] );
Run Code Online (Sandbox Code Playgroud)

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

InsertStringsIntoDatabase( ExamineLogFileForX( "c:\OMG.log" ) );
Run Code Online (Sandbox Code Playgroud)

  • 嘿gishu,很好的答案。您给出的示例...不是更多的集成测试...吗?如果是这样,问题仍然存在,如何真正测试虚方法......也许这是不可能的? (2认同)
  • @andy - 取决于您对“集成测试”的定义。命令式方法通常会更改状态,因此您可以通过询问对象状态的单元测试来验证。信息方法可以通过插入模拟侦听器/合作者的单元测试来验证,以确保测试对象发出正确的通知。我认为两者都可以通过单元测试进行合理的测试。 (2认同)

Jon*_*eet 59

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

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

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


Dav*_*itt 28

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

它应该在某个地方改变全球状态(uuh,代码味道!)吗?

它应该调用接口吗?

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

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

应该是 ...?


Sua*_*ere 11

Void返回类型/子例程是旧闻.我没有等制成的返回类型为void(除非我是极端懒惰)8年(从这个答案的时间,所以只是有点被问这个问题之前).

而不是像这样的方法:

public void SendEmailToCustomer()
Run Code Online (Sandbox Code Playgroud)

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

public bool TrySendEmailToCustomer()
Run Code Online (Sandbox Code Playgroud)

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

此外,布尔不是唯一的州类型.以前制作的子程序有多次实际上可以返回三种或更多种不同的状态(Good,Normal,Bad等).在那些情况下,你只是使用

public StateEnum TrySendEmailToCustomer()
Run Code Online (Sandbox Code Playgroud)

然而,尽管Try-Paradigm在某种程度上回答了关于如何测试void返回的问题,但还有其他考虑因素.例如,在/一个"TDD"周期之后,你会"重构",并注意你正在做的两件事情与你的方法......从而打破了"单一职责原则." 所以应该先照顾好.其次,你可能已经认识到依赖...你正在触及"持久"数据.

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

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

public <Constructor/MethodName> (IBusinessDataEtc otherLayerOrTierObject, string[] stuffToInsert)
Run Code Online (Sandbox Code Playgroud)

现在,你可以接受你的业务/数据层对象的接口,你可以在单元测试嘲笑它,并没有相关性或害怕"意外"集成测试.

因此,在您的实时代码中,您传入一个REAL IBusinessDataEtc对象.但是在单元测试中,您传入一个MOCK IBusinessDataEtc对象.在该Mock中,您可以包含非接口属性,int XMethodWasCalledCount或者在调用接口方法时更新其状态的内容.

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

  1. 现在是Try-Paradigm方法的"子程序"的状态.
  2. 你的Mock IBusinessDataEtc对象的状态.

有关构建级别的依赖注入思想的更多信息......因为它们与单元测试有关...请查看构思器设计模式.它为您拥有的每个当前接口/类增加了一个接口和类,但它们非常小,并且为更好的单元测试提供了巨大的功能增加.


Rey*_*gle 11

你甚至可以这样尝试:

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


小智 6

尝试这个:

[TestMethod]
public void TestSomething()
{
    try
    {
        YourMethodCall();
        Assert.IsTrue(true);
    }
    catch {
        Assert.IsTrue(false);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 欢迎使用 StackOverflow!请考虑在您的代码中添加一些解释。谢谢你。 (2认同)
  • [`ExpectedAttribute`](https://msdn.microsoft.com/zh-cn/library/microsoft.visualstudio.testtools.unittesting.expectedexceptionattribute.aspx)旨在使此测试更加清晰。 (2认同)

Kei*_*las 5

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


Dav*_*rno 5

大概该方法做了一些事情,并且不只是返回?

假设情况是这样,那么:

  1. 如果它修改了其所有者对象的状态,那么您应该测试状态是否正确更改。
  2. 如果它接受某个对象作为参数并修改该对象,那么您应该测试该对象是否被正确修改。
  3. 如果在某些情况下抛出异常,请测试这些异常是否正确抛出。
  4. 如果其行为根据其自身对象或其他对象的状态而变化,请预设状态并通过上述三种测试方法之一测试该方法是否具有正确的 I)。

如果您让我们知道该方法的作用,我可以更具体。