Как вызвать исключение при модульном тестировании простым методом
У меня есть оболочка вызова службы, которая ничего не делает, кроме как перенаправляет параметр в службу при вызове. Причиной для обертки является то, что мы можем впрыскивать обертку, используя 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);
}
}
}
Я должен написать следующие юнит-тесты
- GetLocalWeather() не вызывает сбой приложения при возникновении внутреннего исключения
- 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.
Есть также, вероятно, другие решения по подделке / подделке, но я не сразу знаю о них.