STW
STW

Reputation: 46366

Can a mock be configured to throw a specified exception on any method call?

I have an interface which can't be modified and has a ridiculous amount of methods (basically overloads--each is slightly different).

In some scenarios every method on the interface should throw the same exception. There are several of these scenarios, each with their own exception (stuff like SystemInMaintenanceModeException, ClientRateLimitedException, TheJanitorUnpluggedTheServerException).

There are unit tests and the amount of setup for these exception-throwing scenarios feels downright silly... something like:

_mockedService.Setup(mock => mock.DoA(It.IsAny<string>()).Throws(expectedException);
_mockedService.Setup(mock => mock.DoB(It.IsAny<string>()).Throws(expectedException);
_mockedService.Setup(mock => mock.DoC(It.IsAny<string>()).Throws(expectedException);
...
_mockedService.Setup(mock => mock.DoX(It.IsAny<string>()).Throws(expectedException);
_mockedService.Setup(mock => mock.DoY(It.IsAny<string>()).Throws(expectedException);
_mockedService.Setup(mock => mock.DoZ(It.IsAny<string>()).Throws(expectedException);

Can Moq be configured to throw a specified exception for every method on the mocked interface?

PS: I'm aware that the Strict behavior will throw an exception on any call, but I need it to be specified exception(s).

Upvotes: 4

Views: 884

Answers (3)

rgvlee
rgvlee

Reputation: 3193

There isn't an easy way to do this in Moq that I know of. Built in support would be via DefaultValueProvider, but that'll only work for methods that return a value.

public class DefaultException : DefaultValueProvider
{
    protected override object GetDefaultValue(Type type, Mock mock)
    {
        throw new InvalidOperationException("asdf");
    }
}

...

var fooMock = new Mock<IFoo>();
fooMock.DefaultValueProvider = new DefaultException();
var foo = fooMock.Object;
            
Invoking(() => foo.Bar()).Should().Throw<InvalidOperationException>("asdf");

I've used IInterceptor as mentioned in a another answer here and it'll work as described. My usage was over the Mock<T> itself so required a bit more care, but over the target interface directly should present nothing unusual.

Holistically this feels like a problem that AutoMoq could solve, with some customisation around the method set up. I couldn't find a ready made solution but it'd be what I'd look into if I wanted a pure solution to the problem.

Upvotes: 1

silkfire
silkfire

Reputation: 25935

I would recommend using FakeItEasy, it has great support for this scenario:

A.CallTo(_service).Throws(expectedException)

This will make any method on the mock throw the specified exception.

https://fakeiteasy.readthedocs.io/en/stable/specifying-a-call-to-configure/#specifying-a-call-to-any-method-or-property

Upvotes: 2

Viktor Arsanov
Viktor Arsanov

Reputation: 1583

Moq uses Castle DynamicProxy, so you could use it directly like this

using System;
using System.Threading.Tasks;
using Castle.DynamicProxy;

public class Interceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            throw new NotImplementedException();

        }
    }

    public interface ITest
    {
        void Test1();
        Task Test2(string a);
        int Add(int a1, int a2);
    }

    class Program
    {
        static void Main(string[] args)
        {
            {
                ProxyGenerator generator = new ProxyGenerator();
                var c = generator.CreateInterfaceProxyWithoutTarget<ITest>(new Interceptor());
                try
                {
                    var r = c.Add(11, 22);

                    Console.WriteLine(r);
                }
                catch (NotImplementedException e)
                {

                }
                c.Test2("");
                Console.ReadKey();
            }
            Console.WriteLine("Hello World!");
        }
    }

Also check this question

For Task returning methods you would probably need extra logic like to return Task.FromException.

Upvotes: 2

Related Questions