C# 单元测试无效方法?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/246038/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 19:41:41  来源:igfitidea点击:

Unit testing void methods?

c#unit-testing

提问by jdiaz

What is the best way to unit test a method that doesn't return anything? Specifically in c#.

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

What I am really trying to test is a method that takes a log file and parses it for specific strings. The strings are then inserted into a database. Nothing that hasn't been done before but being VERY new to TDD I am wondering if it is possible to test this or is it something that doesn't really get tested.

我真正想要测试的是一种获取日志文件并将其解析为特定字符串的方法。然后将字符串插入到数据库中。没有什么以前没有做过,但对 TDD 非常陌生

采纳答案by Gishu

If a method doesn't return anything, it's either one of the following

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

  • imperative- You're either asking the object to do something to itself.. e.g change state (without expecting any confirmation.. its assumed that it will be done)
  • informational- just notifying someone that something happened (without expecting action or response) respectively.
  • 命令式- 你要么要求对象对自己做某事..例如改变状态(不期待任何确认..它假设它会完成)
  • 信息- 分别通知某人某事发生(不期望采取行动或回应)。

Imperative methods - you can verify if the task was actually performed. Verify if state change actually took place. e.g.

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

void DeductFromBalance( dAmount ) 

can be tested by verifying if the balance post this message is indeed less than the initial value by dAmount

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

Informational methods - are rare as a member of the public interface of the object... hence not normally unit-tested. However if you must, You can verify if the handling to be done on a notification takes place. e.g.

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

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

can be tested by verifying if the email is being sent

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

Post more details about your actual method and people will be able to answer better.
Update: Your method is doing 2 things. I'd actually split it into two methods that can now be independently tested.

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

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

String[] can be easily verified by providing the first method with a dummy file and expected strings. The second one is slightly tricky.. you can either use a Mock (google or search stackoverflow on mocking frameworks) to mimic the DB or hit the actual DB and verify if the strings were inserted in the right location. Check this threadfor some good books... I'd recomment Pragmatic Unit Testing if you're in a crunch.
In the code it would be used like

String[] 可以通过为第一种方法提供一个虚拟文件和预期字符串来轻松验证。第二个有点棘手..您可以使用 Mock(在模拟框架上使用 Google 或搜索 stackoverflow)来模拟 DB 或点击实际 DB 并验证字符串是否插入到正确的位置。检查此线程以获取一些好书...如果您处于紧要关头,我会推荐实用单元测试。
在代码中,它将像这样使用

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

回答by Keith Nicholas

it will have some effect on an object.... query for the result of the effect. If it has no visible effect its not worth unit testing!

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

回答by André

Depends on what it's doing. If it has parameters, pass in mocks that you could ask later on if they have been called with the right set of parameters.

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

回答by David Schmitt

As always: test what the method is supposed to do!

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

Should it change global state (uuh, code smell!) somewhere?

它是否应该在某处更改全局状态(呃,代码异味!)?

Should it call into an interface?

它应该调用接口吗?

Should it throw an exception when called with the wrong parameters?

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

Should it throw no exception when called with the right parameters?

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

Should it ...?

应该是 ...?

回答by David Arno

Presumably the method does something, and doesn't simply return?

大概该方法做了一些事情,而不是简单地返回?

Assuming this is the case, then:

假设是这种情况,那么:

  1. If it modifies the state of it's owner object, then you should test that the state changed correctly.
  2. If it takes in some object as a parameter and modifies that object, then your should test the object is correctly modified.
  3. If it throws exceptions is certain cases, test that those exceptions are correctly thrown.
  4. 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).
  1. 如果它修改了它的所有者对象的状态,那么您应该测试状态是否正确更改。
  2. 如果它接受某个对象作为参数并修改该对象,那么您应该测试该对象是否被正确修改。
  3. 如果在某些情况下抛出异常,请测试这些异常是否正确抛出。
  4. 如果它的行为根据它自己的对象或某个其他对象的状态而变化,则预设状态并通过上述三种测试方法之一来测试该方法是否正确)。

If youy let us know what the method does, I could be more specific.

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

回答by Jon Skeet

Test its side-effects. This includes:

测试其副作用。这包括:

  • Does it throw any exceptions? (If it should, check that it does. If it shouldn't, try some corner cases which might if you're not careful - null arguments being the most obvious thing.)
  • Does it play nicely with its parameters? (If they're mutable, does it mutate them when it shouldn't and vice versa?)
  • Does it have the right effect on the state of the object/type you're calling it on?
  • 它会抛出任何异常吗?(如果应该,请检查它是否确实如此。如果不应该,请尝试一些极端情况,如果您不小心,可能会出现这种情况 - 空参数是最明显的事情。)
  • 它的参数能很好地发挥作用吗?(如果它们是可变的,它会在不应该改变它们的时候改变它们,反之亦然吗?)
  • 它对您调用它的对象/类型的状态有正确的影响吗?

Of course, there's a limit to how muchyou can test. You generally can't test with every possible input, for example. Test pragmatically - enough to give you confidence that your code is designed appropriately and implemented correctly, and enough to act as supplemental documentation for what a caller might expect.

当然,还有如何限制多少你可以测试一下。例如,您通常无法对所有可能的输入进行测试。务实地测试 - 足以让您确信您的代码设计得当并正确实现,并且足以作为调用者可能期望的补充文档。

回答by dove

Use Rhino Mocksto set what calls, actions and exceptions might be expected. Assuming you can mock or stub out parts of your method. Hard to know without knowing some specifics here about the method, or even context.

使用Rhino Mocks设置可能会出现的调用、操作和异常。假设您可以模拟或删除部分方法。如果不了解该方法的一些细节,甚至上下文,就很难知道。

回答by Suamere

Void return types / Subroutines are old news. I haven't made a Void return type (Unless I was being extremely lazy) in like 8 years (From the time of this answer, so just a bit before this question was asked).

无效返回类型/子程序是旧新闻。我已经有大约 8 年没有做过 Void 返回类型(除非我非常懒惰)(从这个答案开始,所以就在问这个问题之前)。

Instead of a method like:

而不是像这样的方法:

public void SendEmailToCustomer()

Make a method that follows Microsoft's int.TryParse() paradigm:

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

public bool TrySendEmailToCustomer()

Maybe there isn't any information your method needs to return for usage in the long-run, but returning the state of the method after it performs its job is a huge use to the caller.

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

Also, bool isn't the only state type. There are a number of times when a previously-made Subroutine could actually return three or more different states (Good, Normal, Bad, etc). In those cases, you'd just use

此外, bool 不是唯一的状态类型。有很多时候,以前制作的子程序实际上可以返回三个或更多不同的状态(好、正常、坏等)。在这些情况下,您只需使用

public StateEnum TrySendEmailToCustomer()

However, while the Try-Paradigm somewhat answers this question on how to test a void return, there are other considerations too. For example, during/after a "TDD" cycle, you would be "Refactoring" and notice you are doing two things with your method... thus breaking the "Single Responsibility Principle." So that should be taken care of first. Second, you might have idenetified a dependency... you're touching "Persistent" Data.

然而,虽然 Try-Paradigm 在某种程度上回答了这个关于如何测试无效回报的问题,但也有其他的考虑。例如,在“TDD”周期期间/之后,您将“重构”并注意到您正在用您的方法做两件事……从而打破了“单一职责原则”。所以应该先处理。其次,您可能已经确定了一个依赖项……您正在接触“持久”数据。

If you are doing the data access stuff in the method-in-question, you need to refactor into an n-tier'd or n-layer'd architecture. But we can assume that when you say "The strings are then inserted into a database", you actually mean you're calling a business logic layer or something. Ya, we'll assume that.

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

When your object is instantiated, you now understand that your object has dependencies. This is when you need to decide if you are going to do Dependency Injection on the Object, or on the Method. That means your Constructor or the method-in-question needs a new Parameter:

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

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

Now that you can accept an interface of your business/data tier object, you can mock it out during Unit Tests and have no dependencies or fear of "Accidental" integration testing.

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

So in your live code, you pass in a REAL IBusinessDataEtcobject. But in your Unit Testing, you pass in a MOCK IBusinessDataEtcobject. In that Mock, you can include Non-Interface Properties like int XMethodWasCalledCountor something whose state(s) are updated when the interface methods are called.

因此,在您的实时代码中,您传入了一个 REALIBusinessDataEtc对象。但是在您的单元测试中,您传入了一个 MOCKIBusinessDataEtc对象。在该 Mock 中,您可以包含非接口属性,例如int XMethodWasCalledCount调用接口方法时更新其状态的内容。

So your Unit Test will go through your Method(s)-In-Question, perform whatever logic they have, and call one or two, or a selected set of methods in your IBusinessDataEtcobject. When you do your Assertions at the end of your Unit Test you have a couple of things to test now.

所以你的单元测试将通过你的方法(s)-问题,执行它们拥有的任何逻辑,并调用一个或两个,或IBusinessDataEtc对象中的一组选定方法。当你在单元测试结束时做你的断言时,你现在有几件事要测试。

  1. The State of the "Subroutine" which is now a Try-Paradigm method.
  2. The State of your Mock IBusinessDataEtcobject.
  1. “子程序”的状态,现在是一个 Try-Paradigm 方法。
  2. 你的 MockIBusinessDataEtc对象的状态。

For more information on Dependency Injection ideas on the Construction-level... as they pertain to Unit Testing... look into Builder design patterns. It adds one more interface and class for each current interface/class you have, but they are very tiny and provide HUGE functionality increases for better Unit-Testing.

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

回答by Nathan Alard

Try this:

尝试这个:

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

回答by Shreya Kesharkar

What ever instance you are using to call the void method , You can just use ,Verfiy

您用来调用 void 方法的任何实例,您都可以使用,Verfiy

For Example:

例如:

In My case its _Logis the instance and LogMessageis the method to be tested:

在我的情况下,它_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);
}

Is the Verifythrows an exception due to failure of the method the test would Fail ?

是否Verify由于测试失败的方法失败而引发异常?