fahadash
fahadash

Reputation: 3281

How to induce an exception thrown when unit testing a simple method

I have a service call wrapper that does nothing but forwards the parameter to a service in a call. The reason for the wrapper is so we can have the Wrapper Injected using a DI container and thus mocked for unit testing.

Here is how the wrapper looks like

public class WeatherChannelWrapper : IWeatherServiceWrapper
{
    public GetLocalWeather(string zipcode)
    {
        TWCProxy.GetCurrentCondition(zipcode, DateTime.Now.ToString("MM/dd/yyyy hh:mm"));
    }
}

Everything works fine, now I have a requirement to swallow exceptions on TWCProxy's crashes. So now my wrapper looks like this.

public class WeatherChannelWrapper : IWeatherServiceWrapper
{
    private readonly IExceptionLogger exceptionLogger;

    public WeatherChannelWrapper(IExceptionLogger logger)
    {
        this.exceptionLogger = logger;
    }
    public GetLocalWeather(string zipcode)
    {
        try 
        {
            TWCProxy.GetCurrentCondition(zipcode, DateTime.Now.ToString("MM/dd/yyyy hh:mm"));
        }
        catch (Exception e)
        {
            exceptionLogger.Log(e);
        }
    }
}

I have to write the following Unit Tests

  1. GetLocalWeather() does not crash the application when an internal exception occurs
  2. GetLocalWeather() logs exceptions

Now to test both scenarios, I need to somehow induce a crash; how do I do that using NUnit and Automoq/Moq?

Upvotes: 1

Views: 2607

Answers (2)

Lassi Autio
Lassi Autio

Reputation: 1239

Change WeatherChannelWrapper so that you can overwrite it to throw an exception. For example by adding protected virtual method like CallTwcProxy below:

public class WeatherChannelWrapper : IWeatherServiceWrapper
{
    private readonly IExceptionLogger exceptionLogger;

    public WeatherChannelWrapper(IExceptionLogger logger)
    {
        this.exceptionLogger = logger;
    }

    public GetLocalWeather(string zipcode)
    {
        try 
        {
            CallTwcProxy(zipcode);
        }
        catch (Exception e)
        {
            exceptionLogger.Log(e);
        }
    }

    protected virtual void CallTwcProxy(string zipcode)
    {
        TWCProxy.GetCurrentCondition(zipcode, DateTime.Now.ToString("MM/dd/yyyy hh:mm"));
    }
}

Then you can fake it to throw an exception by overriding CallTwcProxy method in your test project:

public class FakeWeatherChannelWrapper : WeatherChannelWrapper
{
    protected override virtual void CallTwcProxy(string zipcode)
    {
        throw new Exception("force exception to throw");
    }
}

Now you have a fake WeatherChannelWrapper which throws an exception which you can use in your test:

public class WeatherChannelWrapperTests
{
    [Test]
    public void GetLocalWeather_WhenInternalExceptionOccurs_LogsException_Test()
    {
        var loggerMock = new Mock<IExceptionLogger>();
        var sut = new FakeWeatherChannelWrapper(loggerMock.Object);

        sut.GetLocalWeather("12345");

        // Assert Exception is logged
        loggerMock.Verify(logger => logger.Log(It.IsAny<Exception>()), Times.Once);

        // And practically this also tests that it doesn't crash even if exception is thrown
    }
}

Upvotes: 1

AndrewP
AndrewP

Reputation: 1618

Without restructuring your code, what you need to do is to use a shim on the TWCProxy class. Moq does not provide shimming, so you may need to use a third party library.

I am not sure if you have access to use something like Microsoft Fakes, but if you do, this can be achieved quite easily in your unit test.

https://msdn.microsoft.com/en-us/library/hh549175.aspx

Firstly, reference your 3rd party library and create a Fake the lib. In VS (I use enterprise, not sure what versions have the feature) this can be done in your test project, by referencing the library, then in the References, right clicking the library and Add Fake Assembly. Then build your project.

Then, in your unit test, you can set up the shim as follows

[TestMethod]
public void MyTestMethod()
{
    using (ShimsContext.Create())
    {
        // fake assembly will have a long method name for the signature of the call. omitted for simplicity
        ShimTWCProxy.GetCurrentCondition[...] = () => {throw new Exception("message")};

        // instantiate and execute test code
        var logger = new Mock<IExceptionLogger>();
        var testedClass = new WeatherChannelWrapper (logger.Object);
        testedClass.GetLocalWeather("...");
        svc.Verify(x => x.Log(It.IsAny<string>()), Times.Once);

    }
}

Another approach would be to pass into the class a method delegate, which could default to your static class, and then allow you to override it at run time, although this might be a bit messy. Let me know if you want an example.

Please note that MS Fakes doesn't play nicely with some test runners/frameworks. For example, it seems to work with NUnit with MSTest adapter, and with MSTest and Resharper test runner, but not NUnit and Resharper runner.

There are also probably other shimming/faking solutions, but I am not immediately aware of them.

Upvotes: 1

Related Questions