2012-05-15 14 views
36

que necesito para probar el siguiente método:El uso Moq para verificar las llamadas se realizan en el orden correcto

CreateOutput(IWriter writer) 
{ 
    writer.Write(type); 
    writer.Write(id); 
    writer.Write(sender); 

    // many more Write()s... 
} 

He creado un Moq'd IWriter y quiero asegurar que los métodos se llaman en Write() el orden correcto

Tengo el siguiente código de prueba:

var mockWriter = new Mock<IWriter>(MockBehavior.Strict); 
var sequence = new MockSequence(); 
mockWriter.InSequence(sequence).Setup(x => x.Write(expectedType)); 
mockWriter.InSequence(sequence).Setup(x => x.Write(expectedId)); 
mockWriter.InSequence(sequence).Setup(x => x.Write(expectedSender)); 

Sin embargo, la segunda llamada a Write() en CreateOutput() (para escribir el valor id) lanza un MockException con el mensaje "IWriter.Write() ha fallado con la invocación simulacro de comportamiento Estricto. Todas las invocaciones en el simulacro deben tener una configuración correspondiente. ".

También me resulta difícil encontrar documentación/ejemplos definitivos y actualizados de las secuencias de Moq.

¿Estoy haciendo algo mal o no puedo configurar una secuencia con el mismo método? Si no es así, ¿hay alguna alternativa que pueda usar (preferentemente con Moq/NUnit)?

+0

posible duplicado de [Cómo probar orden llamada al método con Moq] (http://stackoverflow.com/questions/1765738/how- to-test-method-call-order-with-moq) – sloth

+0

La [última versión de Moq, v4.2] (https://github.com/Moq/moq) ha "mejorado la simulación de pruebas de secuencia de invocación" de acuerdo con su [notas de la versión] (https://github.com/Moq/moq4/blob/master/ReleaseNotes.md). –

+1

Estoy usando un v.4.2.x y puedo confirmar que la funcionalidad de secuencia me funciona. –

Respuesta

38

Hay error cuando using MockSequence on same mock. Definitivamente se solucionará en versiones posteriores de la biblioteca Moq (también se puede arreglar manualmente al cambiar la implementación de Moq.MethodCall.Matches).

Si desea utilizar Moq solamente, entonces se puede comprobar el orden de llamada de método a través de devoluciones de llamada:

int callOrder = 0; 
writerMock.Setup(x => x.Write(expectedType)).Callback(() => Assert.That(callOrder++, Is.EqualTo(0))); 
writerMock.Setup(x => x.Write(expectedId)).Callback(() => Assert.That(callOrder++, Is.EqualTo(1))); 
writerMock.Setup(x => x.Write(expectedSender)).Callback(() => Assert.That(callOrder++, Is.EqualTo(2))); 
+1

Gracias por la información, he recurrido al uso de este método. Espero que haya versiones posteriores de Moq! Una de las mejores herramientas de prueba es ... –

+1

Una palabra de advertencia aquí, si nunca se invoca el método 'Write()', ninguna de estas devoluciones de llamadas tendrá la posibilidad de afirmar nada. Asegúrese de agregar una verificación catch all que el método se invoque al menos una vez. –

+0

Existe un debate en curso sobre github con respecto a la secuenciación, para cualquier persona que quiera contribuir/comentar: https://github.com/moq/moq4/issues/75 – Ray

0

Sospecho que expectedId no es lo que esperas.

Sin embargo, probablemente solo escribiría mi propia implementación de IWriter para verificar en este caso ... probablemente sea mucho más fácil (y más fácil de cambiar más adelante).

Lo sentimos, no hay ningún consejo Moq directamente. Me encanta, pero no he hecho esto en eso.

¿Quizás necesites agregar .Verify() al final de cada configuración? (Eso realmente es una suposición aunque tengo miedo).

+0

He verificado dos veces y los valores son correctos y como se esperaba. Hacer rodar mis propios burla es algo que trato de evitar ahora que uso Moq, ¡pero puede que tenga que recurrir a él! Y Verify() no se puede agregar después de la configuración, solo Verificable() - lo que lamentablemente no hace diferencia. Gracias por los indicadores. –

8

he logrado obtener el comportamiento que quiero, pero requiere la descarga de una biblioteca de 3 ª parte de http://dpwhelan.com/blog/software-development/moq-sequences/

La secuencia a continuación, se puede probar usando la siguiente:

var mockWriter = new Mock<IWriter>(MockBehavior.Strict); 
using (Sequence.Create()) 
{ 
    mockWriter.Setup(x => x.Write(expectedType)).InSequence(); 
    mockWriter.Setup(x => x.Write(expectedId)).InSequence(); 
    mockWriter.Setup(x => x.Write(expectedSender)).InSequence(); 
} 

tengo Agregué esto como una respuesta en parte para ayudar a documentar esta solución, pero todavía estoy interesado en si se puede lograr algo similar usando solo Moq 4.0.

No estoy seguro de si Moq todavía está en desarrollo, pero solucionar el problema con MockSequence o incluir la extensión moq-sequences en Moq sería bueno de ver.

+0

es una pena que no puedas. Cool no sabía que existía: D. Pero en realidad te encuentras tropezando una vez que te adentras en este territorio, y quizás por qué Moq falta en esta área, y sí, el proyecto parece muy tranquilo. Vimos sus problemas y la secuencia ha estado allí por 4 años pendientes. También noté que la última versión tiene final en su nombre. –

3

Recientemente, he creado dos características para Moq: VerifyInSequence() y VerifyNotInSequence(). Funcionan incluso con Loose Mocks.Sin embargo, estos son sólo está disponible en un repositorio tenedor moq:

https://github.com/grzesiek-galezowski/moq4

y esperan más comentarios y las pruebas antes de decidir si pueden ser incluidos en releaase oficial moq. Sin embargo, nada le impide descargar la fuente como ZIP, compilarla en un dll y probarla. El uso de estas características, la verificación de la secuencia que necesita podría escribirse como tal:

 
var mockWriter = new Mock<IWriter>() { CallSequence = new LooseSequence() }; 

//perform the necessary calls 

mockWriter.VerifyInSequence(x => x.Write(expectedType)); 
mockWriter.VerifyInSequence(x => x.Write(expectedId)); 
mockWriter.VerifyInSequence(x => x.Write(expectedSender)); 

(nota que se pueden utilizar otras dos secuencias, dependiendo de sus necesidades secuencia floja permitirá ninguna llamada entre las que desea verificar. StrictSequence no permitirá esto y StrictAnytimeSequence es como StrictSequence (no hay llamadas de método entre llamadas verificadas), pero permite que la secuencia sea precedida por cualquier cantidad de llamadas arbitrarias

Si decide probar esta característica experimental, por favor comente con sus pensamientos en: https://github.com/Moq/moq4/issues/21

Gracias!

7

Escribí un método de extensión que hará valer según el orden de invocación.

public static class MockExtensions 
{ 
    public static void ExpectsInOrder<T>(this Mock<T> mock, params Expression<Action<T>>[] expressions) where T : class 
    { 
    // All closures have the same instance of sharedCallCount 
    var sharedCallCount = 0; 
    for (var i = 0; i < expressions.Length; i++) 
    { 
     // Each closure has it's own instance of expectedCallCount 
     var expectedCallCount = i; 
     mock.Setup(expressions[i]).Callback(
     () => 
      { 
      Assert.AreEqual(expectedCallCount, sharedCallCount); 
      sharedCallCount++; 
      }); 
    } 
    } 
} 

Funciona aprovechando la forma en que funcionan los cierres con respecto a las variables de ámbito. Como solo hay una declaración para sharedCallCount, todos los cierres tendrán una referencia a la misma variable. Con expectedCallCount, una instancia nueva se instancia cada iteración del ciclo (en lugar de simplemente usar i en el cierre). De esta forma, cada cierre tiene una copia de mi alcance solo para compararlo con sharedCallCount cuando se invocan las expresiones.

Aquí hay una pequeña prueba de unidad para la extensión. Tenga en cuenta que este método se llama en su sección de configuración, no en su sección de afirmación.

[TestFixture] 
public class MockExtensionsTest 
{ 
    [TestCase] 
    { 
    // Setup 
    var mock = new Mock<IAmAnInterface>(); 
    mock.ExpectsInOrder(
     x => x.MyMethod("1"), 
     x => x.MyMethod("2")); 

    // Fake the object being called in order 
    mock.Object.MyMethod("1"); 
    mock.Object.MyMethod("2"); 
    } 

    [TestCase] 
    { 
    // Setup 
    var mock = new Mock<IAmAnInterface>(); 
    mock.ExpectsInOrder(
     x => x.MyMethod("1"), 
     x => x.MyMethod("2")); 

    // Fake the object being called out of order 
    Assert.Throws<AssertionException>(() => mock.Object.MyMethod("2")); 
    } 
} 

public interface IAmAnInterface 
{ 
    void MyMethod(string param); 
} 
2

La solución más simple sería utilizar un Queue:

var expectedParameters = new Queue<string>(new[]{expectedType,expectedId,expectedSender}); 
mockWriter.Setup(x => x.Write(expectedType)) 
      .Callback((string s) => Assert.AreEqual(expectedParameters.Dequeue(), s)); 
Cuestiones relacionadas