2013-05-24 4 views
3

나는 Mark Seeman의 article on auto-mocking을 읽었으며 그 기사를 기반으로 재사용 가능한 윈저 컨테이너를 작성하고 있습니다.SUT Automocking

은 (기본적으로 직접 복사) 마크의 기사

의 내 구현의 주요 작업은 AutoMoqResolver 클래스에서 이루어집니다.

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

그럼 내 용기는 단순히 실행 :

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

AutoMoqResolverIWindsorInstaller 인터페이스의 다음과 같은 구현을 사용하여 컨테이너에 추가됩니다 클래스가 인터페이스에 대한 종속성이있을 때마다이 모의를 제공합니다 인스톨러는 단위 테스트에서 인터페이스 의존성을 자동으로 제공합니다.

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()에서 호출되었는지 확인하기 위하여, I 이 같은 새로운 TestableExampleController 클래스를 정의 :

public class TestableExampleController : ExampleController 
{ 
    public TestableExampleController(IService service) : base(service) 
    { 
    } 

    public virtual void PerfomTaskPublic() 
    { 
     base.PerfomTask(); 
    } 

    protected override void PerformTask() 
    { 
     this.PerformTaskPublic(); 
    } 
} 

내가 다음 TES 그래서 내 SUT로 TestableExampleController을 사용할 수 있습니다 t은 통과 할 것이다 :

[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 구현을 사용하여 대부분의 시간을 보냈지만이 기능을 사용할 수는 없습니다.

제 질문은 명확하고 답변은 실제로 비교적 간단합니다.

+2

: http://stackoverflow.com/questions/11958110/technique-for-using-autofixture-to-integration-test-an-application -using-castle AutoFixture를 사용하는 것으로 괜찮 으면 autofixture 태그를 추가 할 수 있습니다. – TrueWill

+0

안녕하세요 닉, Rhino Mock을 사용하기 위해 similair를 사용했습니다. 여기에는 코드가 없지만 Lazy Component Loader를 사용했다는 것을 상기합니다. 이렇게하면 요청 된 모든 종속성을 등록 할 수 있습니다. 따라서 하위 종속 결정자 대신에 이것이 효과가 있는지 알 수 있습니다. – Marwijn

답변

5

AutoFixture.AutoMoq는 내가 상자에서 원하는 것을 정확히 수행 할 것입니다. 올바른 방향으로 나를 가리켜 주신 TrueWill에게 감사드립니다.

다음의 간단한 테스트는 통과 할 것이다 :이 도움이 될

[TestMethod] 
public void Run_Calls_DoSomethingProtected() 
{ 
    //// Arrange 
    // AutoMoqCustomization allows AutoFixture to 
    // be used an an auto-mocking container 
    var fixture = new Fixture().Customize(new AutoMoqCustomization()); 

    // simply ask the fixture to create a mock 
    var sutMock = fixture.Create<Mock<TestableDummySystem>>(); 

    //// Act 
    // exercise the mock object 
    sutMock.Object.Run(); 

    //// Assert 
    // this verification passes! 
    sutMock.Verify(x => x.DoSomethingProtectedPublic()); 
}