Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a FakeItEasy faked object's method throw on first invocation and return on second?

I have a method which is invoked multiple times within a foreach loop, each time with the same parameter values.

foreach (var item in myCollection)
{
    // do some stuff with item
    // then...
    var result = _myService.Foo(aConstant, anotherConstant);
    // do something with result
}

I am trying to write a test that ensures the loop continues iterating even if _myService.Foo() throws an exception on the first time through.

In Moq, I am allowed to chain together calls to Returns and Throws like so:

mockService.Setup(x => x.Foo(aConstant, anotherConstant)).Throws<Exception>().Returns(someResult);

This will cause the call to Foo to throw an Exception but all subsequent calls will return someResult. My main goal is to ensure that a try/catch block is wrapped around the second half of code inside my foreach block so that the loop continues even if an Exception occurs.

foreach (var item in myCollection)
{
    // do some stuff with item
    // then...
    try
    {
        var result = _myService.Foo(aConstant, anotherConstant);
        // do something with result
    }
    catch (Exception e)
    {
        // ignore any exceptions here and continue looping
    }
}

How can I accomplish something similar to this in FakeItEasy? Or is there a different (better) strategy I could use for doing this kind of assertion?

like image 760
Jesse Webb Avatar asked Dec 16 '22 04:12

Jesse Webb


2 Answers

You can now chain configurations using 'Then'. You can read more about it in the FakeItEasy documentation for changing behavior between calls.

This is an example:

public interface IFoo
{
    int Do();
}

[Test]
public void ThrowsFirstTime()
{
    var fakeFoo = A.Fake<IFoo>();
    A.CallTo(() => fakeFoo.Do()).Throws<Exception>().Once().Then.Returns(1);

    Assert.Throws<Exception>(()=>fakeFoo.Do());
    int t = fakeFoo.Do();

    A.CallTo(() => fakeFoo.Do()).MustHaveHappened(Repeated.Exactly.Twice);
    Assert.That(t, Is.EqualTo(1));
}
like image 82
Blair Conrad Avatar answered Dec 29 '22 11:12

Blair Conrad


In case it helps anyone else…

I figured out a way to do this for methods with void return type. I used the Invokes() method:

A.CallTo(() => _fakeService.VoidMethod(aConstant, anotherConstant))
                           .Invokes(ThrowExceptionOnFirstInvocation);

Then outside the Test method, I defined the ThrowExceptionOnFirstInvocation function:

private void ThrowExceptionOnFirstInvocation(IFakeObjectCall obj)
{
    if (_numInvocations++ == 0) throw new Exception();
}

private int _numInvocations;

I am still not sure how to do this for a method which returns something though.

like image 38
Jesse Webb Avatar answered Dec 29 '22 09:12

Jesse Webb