2010-01-14 17 views
5

Acabo de comenzar a implementar pruebas unitarias (utilizando xUnit y Moq) en un proyecto mío ya establecido. El proyecto usa ampliamente la inyección de dependencia a través del contenedor de la unidad.Moq y accediendo a los parámetros llamados

Tengo dos servicios A y B. El servicio A es el que se está probando en este caso. El servicio A llama a B y le otorga un delegado a una función interna. Esta 'devolución de llamada' se usa para notificar a A cuando se recibe un mensaje que debe manejar.

por lo tanto una llamadas (donde b es una instancia de servicio B):

b.RegisterHandler(Guid id, Action<byte[]> messageHandler); 

Con el fin de probar el servicio A, que necesito para ser capaz de llamar a messageHandler, ya que esta es la única forma en que se acepta actualmente mensajes.

¿Esto se puede hacer utilizando Moq? es decir. ¿Puedo simular el servicio B, de modo que cuando se invoca RegisterHandler, el valor de messageHandler se transfiere a mi prueba?

¿O tengo que volver a diseñar esto? ¿Hay algún patrón de diseño que debería usar en este caso? ¿Alguien sabe de algún buen recurso en este tipo de diseño?

Respuesta

9

Usted puede obtener una instancia de la devolución de llamada (o cualquier otro parámetro de entrada) mediante el uso de la devolución de llamada (el nombre similitud es incidental) método en el Mock :

[TestMethod] 
public void Test19() 
{ 
    Action<byte[]> callback = null; 

    var bSpy = new Mock<IServiceB>(); 
    bSpy.Setup(b => b.RegisterHandler(It.IsAny<Guid>(), It.IsAny<Action<byte[]>>())) 
     .Callback((Guid g, Action<byte[]> a) => callback = a); 

    var sut = new ServiceA(bSpy.Object); 
    sut.RegisterCallback(); 

    Assert.AreEqual(sut.Do, callback); 
} 

esto funciona cuando serviceâ se define como esto:

public class ServiceA 
{ 
    private readonly IServiceB b; 

    public ServiceA(IServiceB b) 
    { 
     if (b == null) 
     { 
      throw new ArgumentNullException("b"); 
     } 

     this.b = b; 
    } 

    public void RegisterCallback() 
    { 
     this.b.RegisterHandler(Guid.NewGuid(), this.Do); 
    } 

    public void Do(byte[] bytes) 
    { 
    } 
} 
0

Sí, puede configurar el objeto Moq para responder a las operaciones esperadas e inesperadas. Esto es un ejemplo de Visual Studio Test Unidad ...

[TestMethod] 
public void MyTest() 
    { 
     var moqObject = new Mock<ServiceB>(); 

     // Setup the mock object to throw an exception if a certain value is passed to it... 
     moqObject.Setup(b => b.RegisterHandle(unexpectedValue).Throws(new ArgumentException()); 

     // Or, setup the mock object to expect a certain method call... 
     moqObject.Setup(b => b.RegisterHandle(expectedValue)); 

     var serviceA = new ServiceA(moqObject.Object); 

     serviceA.DoSomethingToTest(); 

     // This will throw an exception if an expected operation didn't happen... 
     moqObject.VerifyAll(); 
    }