1

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?

fahadash
  • 3,133
  • 1
  • 30
  • 59
  • Seems like you'd test that by passing an invalid zipcode or whatever would cause `TWCProxy.GetCurrentCondition` to thrown an exception. – juharr Jul 27 '17 at 18:48
  • 1
    https://www.bing.com/search?q=c%23%20moq%20throw ... Or you need to clarify what you are looking for. – Alexei Levenkov Jul 27 '17 at 18:49
  • @juharr Yes, but that will not be a reliable way of throwing exceptions. That way you are predicting/relying-on the behavior of a 3rd party API. – fahadash Jul 27 '17 at 18:49
  • @fahadash In that case you'd abstract the 3rd party app for testing, but of course this is your abstraction. So either move the exception handling out of the abstraction or live with less than 100% coverage. Or I guess you could add another layer of abstraction, but that seems like a bit much to test such simple code. – juharr Jul 27 '17 at 18:51
  • @AlexeiLevenkov If you read the code samples in my question, you would know that your Link is exactly what I am not looking for. You can only mock a method to throw exceptions if they are non-static methods injected through a constructor. – fahadash Jul 27 '17 at 18:51
  • I've read your question, but it was not very unclear that your method is static nor if you actually know how to mock regular methods to throw. There are obviously plenty of existing questions about mocking static methods, but none of them will help you as framework you've listed don't support that to my knowledge... Realistically creating proxy class for your `IWeatherServiceWrapper` that eats exception would be much easier than trying to mock static methods. – Alexei Levenkov Jul 27 '17 at 19:49
  • @AlexeiLevenkov Anyone who can read C# Code can clearly see that `TWCProxy` is not a member of the class, neither does it follow the naming conventions, so the only option left is the `static` methods. – fahadash Jul 27 '17 at 20:08
  • I'm sorry - I visit SO too much - everyone knows that samples in a post never provide complete picture, rarely use default coding conventions and in general not necessary represent the actual question (half joking). It is very hard to guess how far from [MCVE] code sample actually is by just looking at it... or figure out how much OP knows about the topic when no information what they tried or searched for is present in the post. – Alexei Levenkov Jul 27 '17 at 20:34

2 Answers2

1

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.

AndrewP
  • 1,598
  • 13
  • 24
1

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
    }
}
Lassi Autio
  • 1,147
  • 1
  • 13
  • 35