with verifiable unit tests setup objetos mock and c# unit-testing moq

c# - verifiable - verify moq



Cómo probar el orden de las llamadas al método con Moq (6)

Por el momento tengo:

[Test] public void DrawDrawsAllScreensInTheReverseOrderOfTheStack() { // Arrange. var screenMockOne = new Mock<IScreen>(); var screenMockTwo = new Mock<IScreen>(); var screens = new List<IScreen>(); screens.Add(screenMockOne.Object); screens.Add(screenMockTwo.Object); var stackOfScreensMock = new Mock<IScreenStack>(); stackOfScreensMock.Setup(s => s.ToArray()).Returns(screens.ToArray()); var screenManager = new ScreenManager(stackOfScreensMock.Object); // Act. screenManager.Draw(new Mock<GameTime>().Object); // Assert. screenMockOne.Verify(smo => smo.Draw(It.IsAny<GameTime>()), Times.Once(), "Draw was not called on screen mock one"); screenMockTwo.Verify(smo => smo.Draw(It.IsAny<GameTime>()), Times.Once(), "Draw was not called on screen mock two"); }

Pero el orden en el que dibujo mis objetos en el código de producción no importa. Podría hacer uno primero o dos, no importa. Sin embargo, debería importar ya que el orden de sorteo es importante.

¿Cómo se asegura (utilizando Moq) de que los métodos se invoquen en un orden determinado?

Editar

Me deshice de esa prueba. El método de sorteo se ha eliminado de mis pruebas unitarias. Solo tendré que probarlo manualmente. Sin embargo, la reversión del orden se llevó a una clase de prueba separada donde se probó, por lo que no es del todo malo.

Gracias por el enlace sobre la función que están buscando. Espero que se agregue pronto, muy útil.


De lo contrario, podría haber utilizado las funciones de devolución de llamada e incrementar / almacenar un valor de callIndex.


Desde la publicación original, puedo suponer que el método de prueba hace las siguientes llamadas de operaciones:

var screenOne = new Screen(...); var screenTwo = new Screen(...); var screens = new []{screenOne, screenTwo}; var screenManager = new ScreenManager(screens); screenManager.Draw();

Donde la implementación del método ''Draw'' es algo como esto:

public class ScreenManager { public void Draw() { _screens[0].Draw(); _screens[1].Draw(); } }

Desde mi punto de vista, si el orden de las llamadas es muy importante, entonces se debe introducir una estructura adicional (que describa la secuencia) en el sistema.

La implementación más simple: cada pantalla debería conocer su elemento posterior y llamar a su método Draw después de dibujarse a sí mismo:

// 1st version public class Screen(Screen screenSubSequent) { private Screen _screenNext; public Screen(Screen screenNext) { _screenNext=screenNext; } public void Draw() { // draw himself if ( _screenNext!=null ) _screenNext.Draw(); } } public class ScreenManager { public void Draw() { _screens[0].Draw(); } } static void Main() { var screenOne = new Screen(null, ...); var screenTwo = new Screen(screenOne, ...); var screens = new []{screenOne, screenTwo}; var screenManager = new ScreenManager(screens); }

Desde un punto, cada elemento de la Pantalla debe saber un poco sobre otro. Esto no siempre es bueno. Si es así: puedes crear una clase como ''ScreenDrawer''. Este objeto almacenará su propia pantalla y la pantalla subsiguiente (probablemente lo herede de la clase Screen.) Utilizando otros mundos: la clase ''ScreenDrawer'' describe la estructura del sistema. Aquí hay un escenario más simple de implementación:

// 2nd version public class ScreenDrawer { private Screen _screenNext; public ScreenDrawer(Screen screenNext, ...) : base (...) { _screenNext=screenNext; } public void Draw() { // draw himself if ( _screenNext!=null ) _screenNext.Draw(); } } public class ScreenManager { public void Draw() { _screens[0].Draw(); } } static void Main() { var screenOne = new ScreenDrawer(null, ...); var screenTwo = new ScreenDrawer(screenOne, ...); var screens = new []{screenOne, screenTwo}; var screenManager = new ScreenManager(screens); }

El segundo método introduce herencia adicional, pero no requiere que la clase Pantalla sepa sobre su elemento de subsecuencia.

Resumen: ambos métodos realizan llamadas subsecuentes y no requieren pruebas de "secuencia". En su lugar, requieren pruebas si la ''pantalla'' actual llama a otra y prueba si ''ScreenManager'' llama al método ''Dibujar'' del primer elemento en secuencia.

Este enfoque:

  1. Más comprobable (se puede implementar usando la mayoría del marco de prueba sin necesidad de soportar ''pruebas de secuencia'');
  2. Más estable (nadie puede cambiar fácilmente una secuencia: hola necesitará no solo actualizar el código fuente, sino también actualizar algunas pruebas);
  3. Más orientado a objetos (está trabajando con objetos, no con entidades abstractas como ''secuencia'');
  4. Como resultado: mucho más compatible.

Gracias.



Parece que no está implementado actualmente. Ver Issue 24: MockSequence . Este hilo discute el problema.

Puede considerar revisar sus pruebas, sin embargo. En general, creo que el orden de las pruebas conduce a pruebas frágiles, ya que a menudo prueba los detalles de implementación.

EDITAR: No estoy seguro de que esto aborde la pregunta del OP. La respuesta de Lucero puede ser más útil.


Recientemente creé Moq.Sequences, que ofrece la posibilidad de verificar pedidos en Moq. Es posible que desee leer mi post que describe lo siguiente:

  • Admite invocaciones de método, definidores de propiedad y getters.
  • Le permite especificar la cantidad de veces que se debe esperar una llamada específica.
  • Proporciona bucles que le permiten agrupar llamadas en un grupo recurrente.
  • Le permite especificar la cantidad de veces que se debe esperar un ciclo.
  • Las llamadas que se espera que se llamen en secuencia se pueden mezclar con llamadas que se esperan en cualquier orden.
  • Soporte multihilo.

El uso típico se ve así:

[Test] public void Should_show_each_post_with_most_recent_first_using_sequences() { var olderPost = new Post { DateTime = new DateTime(2010, 1, 1) }; var newerPost = new Post { DateTime = new DateTime(2010, 1, 2) }; var posts = new List<Post> { newerPost, olderPost }; var mockView = new Mock<BlogView>(); using (Sequence.Create()) { mockView.Setup(v => v.ShowPost(newerPost)).InSequence(); mockView.Setup(v => v.ShowPost(olderPost)).InSequence(); new BlogPresenter(mockView.Object).Show(posts); } }


Una solución simple usando Moq CallBacks:

[TestMethod] public void CallInOrder() { // Arrange string callOrder = ""; var service = new Mock<MyService>(); service.Setup(p=>p.FirstCall()).Returns(0).CallBack(()=>callOrder += "1"); service.Setup(p=>p.SecondCall()).Returns(0).CallBack(()=>callOrder += "2"); var sut = new Client(service); // Act sut.DoStuff(); // Assert Assert.AreEqual("12", callOrder); }