Я прочитал Mark Seeman's article on auto-mocking, и теперь я пишу повторно используемый контейнер для виндсерфинга на основе этой статьи.Automocking the SUT
Моя реализация статье Марка (в основном скопированы непосредственно)
Основная работа выполняется в AutoMoqResolver
классе. Это позволит издеваться каждый раз, когда класс имеет зависимость от интерфейса:
public class AutoMoqResolver : ISubDependencyResolver
{
private readonly IKernel kernel;
public AutoMoqResolver(IKernel kernel)
{
this.kernel = kernel;
}
public bool CanResolve(
CreationContext context,
ISubDependencyResolver contextHandlerResolver,
ComponentModel model,
DependencyModel dependency)
{
return dependency.TargetType.IsInterface;
}
public object Resolve(
CreationContext context,
ISubDependencyResolver contextHandlerResolver,
ComponentModel model,
DependencyModel dependency)
{
var mockType = typeof(Mock<>).MakeGenericType(dependency.TargetType);
return ((Mock)this.kernel.Resolve(mockType)).Object;
}
}
AutoMoqResolver
добавляется в контейнер, используя следующую реализацию интерфейса IWindsorInstaller
:
public class AutoMockInstaller<T> : IWindsorInstaller
{
public void Install(
IWindsorContainer container,
IConfigurationStore store)
{
container.Kernel.Resolver.AddSubResolver(
new AutoMoqResolver(container.Kernel));
container.Register(Component.For(typeof(Mock<>)));
container.Register(Classes
.FromAssemblyContaining<T>()
.Pick()
.WithServiceSelf()
.LifestyleTransient());
}
}
Тогда мой контейнер просто работает установщик и он готов автоматически предоставлять mocks для любых зависимостей интерфейса в модульных тестах:
public class AutoMockContainer<T> : WindsorContainer
{
public AutoMockContainer()
{
// simply run the auto-mock installer
this.Install(new AutoMockInstaller<T>());
}
}
Супер!
Я протестировал это, и мои зависимости счастливо издеваются над собой, и я затем применил его к какому-то реальному коду. Это когда я понял, что решение не помогает мне из-за шаблона, который я обычно наблюдаю при тестировании класса. Моя особая проблема заключается в том, что я хочу иметь возможность самофиксации самого SUT, чтобы проверить, что один метод на SUT вызывается из другого.
Мой код, который должен быть протестирован
Я объясню себя в качестве примера. Я разрабатываю MVC код и я поддерживаю ненавязчивый AJAX с помощью следующей общей схеме:
public Class ExampleController : Controller
{
private IService service;
public ExampleController(IService service)
{
this.service = service;
}
public PartialViewResult DoSomethingWithAjax()
{
this.PerformTask();
return this.PartialView();
}
public RedirectToRouteResult DoSomethingWithoutAjax()
{
this.PerformTask();
return this.RedirectToAction("SomeAction");
}
protected virtual void PerformTask()
{
// do something here
}
}
Мой тестовый образец
Так что для того, чтобы убедиться в том, что метод PerformTask()
был вызван из DoSomethingWithAjax()
или DoSomethingWithoutAjax()
, я определить новый класс TestableExampleController
так:
public class TestableExampleController : ExampleController
{
public TestableExampleController(IService service) : base(service)
{
}
public virtual void PerfomTaskPublic()
{
base.PerfomTask();
}
protected override void PerformTask()
{
this.PerformTaskPublic();
}
}
можно затем использовать TestableExampleController
как мой SUT так следующим TES т будет проходить:
[TestMethod]
public void DoSomethingAjax_Calls_PerformTask()
{
//// Arrange
// create a mock TestableExampleController
var controllerMock = new Mock<TestableExampleController>();
controllerMock.CallBase = true;
// use the mock controller as the SUT
var sut = controllerMock.Object;
//// Act
sut.DoSomethingAjax();
//// Assert
controllerMock.Verify(x => x.PerformTaskPublic(), Times.Once());
}
Моя проблема
Рефакторинг этот тест, чтобы использовать мой AutoMockContainer
класс, как это не работает:
[TestMethod]
public void DoSomethingAjax_Calls_PerformTask()
{
//// Arrange
// create a container
var container = new AutoMockContainer<TestableExampleController>();
// resolve a mock SUT using the container
var controllerMock = container.Resolve<Mock<TestableExampleController>>();
controllerMock .CallBase = true;
// use the mock controller as the SUT
var sut = controllerMock.Object;
//// Act
sut.DoSomethingAjax();
//// Assert
controllerMock.Verify(x => x.PerformTaskPublic(), Times.Once());
}
тест не удается создать экземпляр Mock<TestableExampleController>
потому что он не может найти конструктор без параметров.
Невозможно создать прокси-класс класса: MyNamespace.TestableExampleController. Не удалось найти конструктор без параметров.Имя Параметр: constructorArguments
Мой Предложенное решение
В идеале я хотел бы реализовать класс-оболочку, которая может быть зарегистрирована с контейнером для автоматического предоставления издеваться для любого компонента:
public class ComponentWrapper<T> where T : class
{
public ComponentWrapper(Mock<T> componentMock)
{
componentMock.CallBase = true;
this.ComponentMock = componentMock;
}
public Mock<T> ComponentMock { get; private set; }
public T Component
{
get { return this.ComponentMock.Object; }
}
}
Я бы хотел (могли) написать следующее тестирование:
[TestMethod]
public void DoSomethingAjax_Calls_PerformTask()
{
//// Arrange
// create a container
var container = new AutoMockContainer<TestableExampleController>();
// resolve a ComponentWrapper using the container
var wrapper = container.Resolve<ComponentWrapper<TestableExampleController>>();
//// Act
// call a method using the component
wrapper.Component.DoSomethingAjax();
//// Assert
// verify a method call using the mock
wrapper.ComponentMock.Verify(x => x.PerformTaskPublic(), Times.Once());
}
Я не могу понять, как это достичь, и большую часть дня я провел с новыми реализациями ISubDependencyResolver, но я просто не могу заставить это работать.
Надеюсь, мой вопрос ясен, и ответ на самом деле относительно прост?
Это может помочь: http://stackoverflow.com/questions/11958110/technique-for-using-autofixture-to-integration-test-an-application -using-castle Если вы в порядке с использованием AutoFixture, вы можете добавить тег автоистории. – TrueWill
Привет, Ник, я сделал что-то подобное для использования с Rhino Mocks. У меня нет кода здесь, но я помню, что использовал Lazy Component Loader. Таким образом, вы можете зарегистрировать любую зависимость после ее запроса. Таким образом, вы можете увидеть, будет ли это работать вместо вас, а не с помощью подзависимости. – Marwijn