Как вызвать исключение при модульном тестировании простым методом

У меня есть оболочка вызова службы, которая ничего не делает, кроме как перенаправляет параметр в службу при вызове. Причиной для обертки является то, что мы можем впрыскивать обертку, используя DI-контейнер и, таким образом, подвергать насмешкам для модульного тестирования.

Вот как выглядит оболочка

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

Все работает нормально, теперь у меня есть требование проглотить исключения при сбоях TWCProxy. Так что теперь моя обертка выглядит так.

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);
        }
    }
}

Я должен написать следующие юнит-тесты

  1. GetLocalWeather() не вызывает сбой приложения при возникновении внутреннего исключения
  2. GetLocalWeather() регистрирует исключения

Теперь, чтобы проверить оба сценария, мне нужно как-то вызвать сбой; Как мне это сделать, используя NUnit и Automoq/Moq?

2 ответа

Измените WeatherChannelWrapper, чтобы вы могли перезаписать его и выдать исключение. Например, добавив защищенный виртуальный метод, такой как CallTwcProxy, ниже:

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"));
    }
}

Затем вы можете подделать его, чтобы вызвать исключение, переопределив метод CallTwcProxy в вашем тестовом проекте:

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

Теперь у вас есть фальшивый WeatherChannelWrapper, который выдает исключение, которое вы можете использовать в своем тесте:

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
    }
}

Без реструктуризации вашего кода, вам нужно использовать шим TWCProxy учебный класс. Moq не обеспечивает шиммирования, поэтому вам может понадобиться использовать стороннюю библиотеку.

Я не уверен, есть ли у вас доступ для использования чего-то вроде Microsoft Fakes, но если вы это сделаете, это может быть достигнуто довольно легко в вашем модульном тесте.

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

Во-первых, обратитесь к вашей сторонней библиотеке и создайте Fake the lib. В VS (я использую Enterprise, не уверен, какие версии имеют функцию) это можно сделать в вашем тестовом проекте, сделав ссылку на библиотеку, затем в разделе "Ссылки", щелкнув правой кнопкой мыши библиотеку и Add Fake Assembly, Тогда постройте свой проект.

Затем, в вашем модульном тесте, вы можете настроить шим

[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);

    }
}

Другой подход состоит в том, чтобы передать в класс делегат метода, который может по умолчанию использовать статический класс, а затем позволить вам переопределить его во время выполнения, хотя это может быть немного грязно. Дайте мне знать, если вы хотите пример.

Обратите внимание, что MS Fakes не очень хорошо работает с некоторыми бегунами / фреймворками. Например, кажется, что он работает с NUnit с адаптером MSTest, а также с тестером MSTest и Resharper, но не с NUnit и Resharper Runner.

Есть также, вероятно, другие решения по подделке / подделке, но я не сразу знаю о них.

Другие вопросы по тегам