Arg.Do() is not firing when expected in a When..Do for void method

只愿长相守 提交于 2019-12-12 18:22:30

问题


I have the below structure in a test of mine, intended to test that a certain log is being called with the right complex argument object, even when it throws an exception which is then wrapped and generally manipulated further. The logThing has a method:

void AddEntry(LogEntry);

So I am using When..Do to make it throw an exception,

public void UnitTest()
{
    // Arrange
    ILogThing logThing = Substitute.For<ILogThing>()
    SystemUnderTest system = new SystemUnderTest(logThing);

    List<LogEntry> actualEntries = new List<LogEntry>();
    LogEntry expectedEntry = GetSomeTestData();

    logThing.When(
        lt => lt.AddEntry(Arg.Do<LogEntry>(r => actualEntries.Add(r)))).Do(
        call => { throw new InvalidOperationException("testMessage"); });

    // Act
    try
    {
        system.DoSomethingWhichLogs(someArgs)
    }
    catch(WrappedException ex)
    {
        // Assert
        Assert.AreEqual(1, actualEntries.Count);
        Assert.AreEqual(actualEntries[0], expectedEntry);
    }
}

However, the expected call to Arg.Do() never happens with this setup.

I have put a breakpoint in the catch block, and used Visual Studio's immediate window to call RecievedCalls<>() on the logThing, and it does have a record of a single call to logThing with the right arguments - it's just that Arg.Do appears to only execute after the When..Do block has finished. Clearly that means that since I am throwing in the When..Do, it never reaches it.

I really didn't expect NSubstitute to order the calls in this way, is that expected behaviour? If so, is there anything I can do to test the incoming argument like this, or should I just put my argument checking into the main When..Do block (which makes it harder to read)?

The system under test does various things to the exception which include wrapping it together with the logEntry, so it -is- useful for me to have all of these checks in one test - I did think about splitting it into two separate tests, but realized that if I did that, I couldn't easily pin down where the incorrect wrapped output was coming from (it could either be the part that originally generates the logEntry, or the part wrapping it) wheras with this pattern I can check to make sure the logThing is receiving what I expect it to. Still, if there's a better way to do that, I'm certainly open to suggestions.


回答1:


The exact order of When..Do and Arg.Do isn't defined, and I wouldn't recommend counting on it as I imagine it could change between versions depending on implementation. (If you have a good reason to define it in a particular order please post your suggestion to the usergroup.)

If you just want to check logThing received the expected LogEntry, you can check the argument after the fact using Arg.Is():

logThing.Received().AddEntry(Arg.Is(expectedEntry));

If you need more complex comparison logic, you can do the same thing but use a method to check the argument:

logThing.Received().AddEntry(Arg.Is<LogEntry>(x => CheckLogEntry(x)));

CheckLogEntry can do whatever checks you require, and you can keep the When..Do throwing as before.

If you need to use the When..Do approach, you can can keep your existing approach but move the capture of the argument into the Do call to ensure the order of calls that you expect:

logThing.When(
    lt => lt.AddEntry(Arg.Any<LogEntry>())).Do(
    call =>
    {
        actualEntries.Add(call.Arg<LogEntry>());
        throw new InvalidOperationException("testMessage");
    });

In terms of suggestions for other ways of testing this, I'm not entirely clear on what you are trying to do here, but if you are having trouble isolating where the wrapped output is coming from maybe you could move this responsibility to another dependency? This would let you substitute for this dependency and control exactly where this happened from the perspective of this test.



来源:https://stackoverflow.com/questions/7587443/arg-do-is-not-firing-when-expected-in-a-when-do-for-void-method

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!