2009-04-07 17 views
18

Acabo de cambiar a Moq y me he encontrado con un problema. Estoy probando un método que crea una nueva instancia de un objeto comercial, establece las propiedades del objeto a partir de los valores de entrada del usuario y llama a un método (SaveCustomerContact) para guardar el nuevo objeto. El objeto comercial se pasa como argumento de ref porque atraviesa una capa remota. Necesito probar que el objeto que se pasa a SaveCustomerContact tiene todas sus propiedades configuradas como se esperaba, pero dado que se instancia como nuevo en el método del controlador, parece que no puedo hacerlo.Verificar el valor del parámetro de referencia con Moq

public void AddContact() { 

    var contact = new CustomerContact() { CustomerId = m_model.CustomerId }; 

    contact.Name = m_model.CustomerContactName; 
    contact.PhoneNumber = m_model.PhoneNumber; 
    contact.FaxNumber = m_model.FaxNumber; 
    contact.Email = m_model.Email; 
    contact.ReceiveInvoiceFlag = m_model.ReceiveInvoiceFlag; 
    contact.ReceiveStatementFlag = m_model.ReceiveStatementFlag; 
    contact.ReceiveContractFlag = m_model.ReceiveContractFlag; 
    contact.EmailFlag = m_model.EmailFlag; 
    contact.FaxFlag = m_model.FaxFlag; 
    contact.PostalMailFlag = m_model.PostalMailFlag; 
    contact.CustomerLocationId = m_model.CustomerLocationId; 

    RemotingHandler.SaveCustomerContact(ref contact); 
} 

Aquí está la prueba:

[TestMethod()] 
public void AddContactTest() { 

    int customerId = 0; 

    string name = "a"; 

    var actual = new CustomerContact(); 

    var expected = new CustomerContact() { 
     CustomerId = customerId, 
     Name = name 
    }; 

    model.Setup(m => m.CustomerId).Returns(customerId); 
    model.SetupProperty(m => model.CustomerContactName, name); 
    model.SetupProperty(m => m.PhoneNumber, string.Empty); 
    model.SetupProperty(m => m.FaxNumber, string.Empty); 
    model.SetupProperty(m => m.Email, string.Empty); 
    model.SetupProperty(m => m.ReceiveInvoiceFlag, false); 
    model.SetupProperty(m => m.ReceiveStatementFlag, false); 
    model.SetupProperty(m => m.ReceiveContractFlag, false); 
    model.SetupProperty(m => m.EmailFlag, false); 
    model.SetupProperty(m => m.FaxFlag, false); 
    model.SetupProperty(m => m.PostalMailFlag, false); 
    model.SetupProperty(m => m.CustomerLocationId, 0); 

    remote 
     .Setup(r => r.SaveCustomerContact(ref actual)) 
     .Callback(() => Assert.AreEqual(actual, expected)); 

    target.AddContact(); 

} 

Esto es sólo el más reciente de muchos intentos de conseguir ahold de ese parámetro. Como referencia, el valor de real no cambia desde su estado inicial (construido).

Mover el Assert.AreEqual (esperado, actual) después de que falla la llamada de destino. Si agrego .Verifiable() a la configuración en lugar de .CallBack y luego llamo a remote.Verify después del objetivo (o, supongo, establezco el simulacro en strict) siempre falla porque el parámetro que proporciono en la prueba no es el misma instancia que la que se creó en el método del controlador.

Estoy usando Moq 3.0.308.2. Cualquier idea sobre cómo probar esto sería apreciada. ¡Gracias!

Respuesta

19

No puedo ofrecerle una solución exacta, pero una alternativa sería ocultar la semántica de paso a paso detrás de un adaptador, que toma el parámetro por valor y lo reenvía al RemotingHandler.Esto sería más fácil burlarse, y eliminaría la "ref" verruga de la interfaz (estoy siempre sospechoso de los parámetros ref :-))

EDIT:

O usted podría utilizar un talón en lugar de una maqueta , por ejemplo:

public class StubRemotingHandler : IRemotingHandler 
{ 
    public CustomerContact savedContact; 

    public void SaveCustomerContact(ref CustomerContact contact) 
    { 
     savedContact = contact; 
    } 
} 

ahora se puede examinar el objeto guardado en su prueba:

IRemotingHandler remote = new StubRemotingHandler(); 
... 
//pass the stub to your object-under-test 
... 
target.AddContact(); 
Assert.AreEqual(expected, remote.savedContact); 

también dice en su comentario:

No me gustaría empezar un precedente de envolver bits aleatorios del backend para que pueda escribir pruebas más fácilmente

creo que eso es exactamente el precedente es necesario establecer! Si tu código no es comprobable, vas a seguir luchando para probarlo. Facilite la prueba y aumente su cobertura.

+0

Supongo que tampoco estoy seguro de cómo lo harías (aunque no sé mucho sobre los stubs). ¿Puedes dar más detalles sobre cómo se podría manejar eso? –

+0

Además, por mucho que esté de acuerdo con usted en el tema de referencia, esa es la manera en que funciona nuestra capa remota y me gustaría comenzar un precedente de envolver bits aleatorios del back-end para poder escribir pruebas más fácilmente. –

+0

Estaba teniendo un problema similar, y me enfoqué tanto en hacer funcionar MoQ, ¡me olvidé de poder escribir mi propio código auxiliar! Gracias. –

9

Lamentablemente, no estoy seguro de que esto sea posible sin el apoyo directo de Moq. El problema es que las expresiones Lambda no son compatibles con ref o out.

"Una expresión lambda no puede capturar directamente una nevera o fuera de parámetros de un método de cerramiento."

http://msdn.microsoft.com/en-us/library/bb397687.aspx

que aun no se puede obtener un ejemplo como la suya para trabajar. No se puede compilar agregar ref a la instalación.

Es posible que desee echa un vistazo a las discusiones Moq para más http://groups.google.com/group/moqdisc

Buena suerte.

+0

¡Excelente respuesta! :) – Delashmate

10

La última versión de Moq admite este escenario.

tomada de la guía de inicio rápido en http://code.google.com/p/moq/wiki/QuickStart:

// ref arguments 
var instance = new Bar(); 
// Only matches if the ref argument to the invocation is the same instance 
mock.Setup(foo => foo.Submit(ref instance)).Returns(true); 
+6

Gracias por la respuesta. Lo he visto en el inicio rápido (de hecho, convencí a mi jefe para que actualizara a la última versión de Moq porque esperaba que funcionara). Lamentablemente, el comportamiento no parece seguir lo que (muy poco claro) sugiere "Solo coincide si el argumento de referencia a la invocación es la misma instancia". –

0

he encontrado problema similar. Bit Obtuve la solución utilizando el último Moq y pasando el valor como

var instance = new Bar(); Mock.Setup (foo => foo.Submit (ref instance)). Devuelve (verdadero);

Anteriormente, estaba usando el mismo método, pero no obtenía el retorno como verdadero.

Dentro de la instancia de la función real se estaba creando y sobrescribir la instancia pasada de la clase de prueba de la unidad estaba causando el problema. Eliminé la creación de instancias dentro de la clase real y luego funcionó.

Espero que te ayude.

gracias

Cuestiones relacionadas