remarks practices para best c# mocking lambda moq

practices - remarks c#



Necesita ayuda para entender mejor a Moq (2)

It.IsAny / It.Is

Estos pueden ser útiles cuando pasa un nuevo tipo de referencia dentro del código bajo prueba. Por ejemplo, si tiene un método similar a:

public void CreatePerson(string name, int age) { Person person = new Person(name, age); _personRepository.Add(person); }

Es posible que desee comprobar que se haya llamado al método add en el repositorio

[Test] public void Create_Person_Calls_Add_On_Repository () { Mock<IPersonRepository> mockRepository = new Mock<IPersonRepository>(); PersonManager manager = new PersonManager(mockRepository.Object); manager.CreatePerson("Bob", 12); mockRepository.Verify(p => p.Add(It.IsAny<Person>())); }

Si desea que esta prueba sea más explícita, puede usarla. Al proporcionar un predicado, la persona debe coincidir

[Test] public void Create_Person_Calls_Add_On_Repository () { Mock<IPersonRepository> mockRepository = new Mock<IPersonRepository>(); PersonManager manager = new PersonManager(mockRepository.Object); manager.CreatePerson("Bob", 12); mockRepository.Verify(pr => pr.Add(It.Is<Person>(p => p.Age == 12))); }

De esta forma, la prueba se realizará a través de una excepción si el objeto persona que se usó para llamar al método add no tenía la propiedad de edad establecida en 12.

Veces

Si tuviera un método similar a:

public void PayPensionContribution(Person person) { if (person.Age > 65 || person.Age < 18) return; //Do some complex logic _pensionService.Pay(500M); }

Una de las cosas que quizás quiera probar es que el método de pago no se llama cuando una persona de más de 65 años ingresa al método

[Test] public void Someone_over_65_does_not_pay_a_pension_contribution() { Mock<IPensionService> mockPensionService = new Mock<IPensionService>(); Person p = new Person("test", 66); PensionCalculator calc = new PensionCalculator(mockPensionService.Object); calc.PayPensionContribution(p); mockPensionService.Verify(ps => ps.Pay(It.IsAny<decimal>()), Times.Never()); }

De manera similar, es posible imaginar situaciones en las que itera sobre una colección y llama a un método para cada elemento de la colección y desea asegurarse de que se haya llamado una cierta cantidad de veces, otras veces simplemente no le importa .

SetupGet / SetupSet

Lo que debes tener en cuenta con estos tipos es que reflejan cómo tu código está interactuando con el simulacro en lugar de cómo estás configurando el simulacro

public static void SetAuditProperties(IAuditable auditable) { auditable.ModifiedBy = Thread.CurrentPrincipal.Identity.Name; }

En este caso, el código establece la propiedad ModifiedBy de la instancia IAuditable mientras obtiene la propiedad Name de la instancia actual de IPrincipal.

[Test] public void Accesses_Name_Of_Current_Principal_When_Setting_ModifiedBy() { Mock<IPrincipal> mockPrincipal = new Mock<IPrincipal>(); Mock<IAuditable> mockAuditable = new Mock<IAuditable>(); mockPrincipal.SetupGet(p => p.Identity.Name).Returns("test"); Thread.CurrentPrincipal = mockPrincipal.Object; AuditManager.SetAuditProperties(mockAuditable.Object); mockPrincipal.VerifyGet(p => p.Identity.Name); mockAuditable.VerifySet(a => a.ModifiedBy = "test"); }

En este caso, estamos configurando la propiedad de nombre en el simulacro de IPrincipal para que devuelva "prueba" cuando se invoca el captador en la propiedad Nombre de Identidad, no configuramos la propiedad en sí.

SetupProperty / SetupAllProperties

Mirando la prueba anterior si fue cambiada para leer

[Test] public void Accesses_Name_Of_Current_Principal_When_Setting_ModifiedBy() { Mock<IPrincipal> mockPrincipal = new Mock<IPrincipal>(); Mock<IAuditable> mockAuditable = new Mock<IAuditable>(); mockPrincipal.SetupGet(p => p.Identity.Name).Returns("test"); var auditable = mockAuditable.Object; Thread.CurrentPrincipal = mockPrincipal.Object; AuditManager.SetAuditProperties(auditable); Assert.AreEqual("test", auditable.ModifiedBy); }

La prueba fallaría. Esto se debe a que el proxy creado por Moq no hace nada en el método set de una propiedad a menos que se lo indique. En efecto, el objeto simulado se parece un poco a esto

public class AuditableMock : IAuditable { public string ModifiedBy { get { return null; } set { } } }

Para que la prueba pase, debes decirle a Moq que configure la propiedad para que tenga el comportamiento estándar de la propiedad. Puedes hacer esto llamando a SetupProperty y el simulacro se parecerá más a

public class AuditableMock : IAuditable { public string ModifiedBy { get; set; } }

y la prueba anterior pasaría ya que el valor "prueba" ahora se almacenará contra el simulacro. Al burlarse de objetos complejos, es posible que desee hacer esto para todas las propiedades, de ahí el acceso directo SetupAllProperties

Finalmente, la bombilla en el IDE es el complemento de resharper.

He estado mirando la documentación de Moq y los comentarios son demasiado cortos para que entienda cada una de las cosas que puede hacer.

Lo primero que no entiendo es It.IsAny<string>(). //example using string It.IsAny<string>(). //example using string

¿Hay una ventaja de usar esto solo por ponerle algún valor? Sé que la gente dice que debes usar esto si no te importa el valor, pero si no te importa el valor, ¿no puedes simplemente hacer "A" o algo así? Esto simplemente parece más escribir.

En segundo lugar, ¿cuándo sería un ejemplo de cuándo no le importaría el valor? Pensé que Moq necesita el valor para hacer coincidir cosas.

No entiendo para qué It.Is<> o cómo usarlo. No entiendo el ejemplo y lo que está tratando de mostrar.

A continuación, no entiendo cuándo usar Times (y sus métodos AtMost y similares). ¿Por qué limitarías la cantidad de veces que algo está configurado? Tengo un valor de AppConfig que necesito usar dos veces. ¿Por qué querría limitarlo a, digamos, una vez? Esto solo haría que la prueba fallara. ¿Esto es para evitar que otras personas agreguen otra a tu código o algo así?

No entiendo cómo usar mock.SetupAllProperties(); ¿Con qué configura las propiedades?

Tampoco entiendo por qué hay tantas formas diferentes de configurar una propiedad y cuáles son sus diferencias. La documentación tiene:

SetupGet(of property) SetupGet<TProperty>

Noté que muchas de las cosas en Moq muestran () y <> - ¿cuál es la diferencia entre ellos y cómo se verían en uso?

Tampoco entiendo por qué tienen SetupGet . ¿No usaría SetupSet para establecer una propiedad? SetupSet tiene cinco formas diferentes de usarlo en la documentación. Además de otra llamada SetupProperty . Entonces no entiendo por qué hay tantos.

En una nota lateral, me pregunto si las variables utilizadas en lambdas son independientes de otras lambdas. P.ej:

mock.setup(m => m.Test); stop.setup(m => m.Test);

¿Sería correcto o habría algún conflicto entre la variable m ?

Finalmente, estaba viendo este video y me pregunto si muestra Visual Studio. Su Intellisense se ve diferente. Aparece una bombilla para él (estoy contento de que la mía no lo haga, ya que me trae recuerdos dolorosos de netbeans), y hay líneas que van desde una abrazadera de apertura hasta la abrazadera de cierre, etc.

Gracias :)


Si no le importa el valor exacto de una propiedad, es mucho mejor usar .IsAny porque está siendo explícito sobre el hecho de que el valor exacto no es importante. Si lo codifica como "abc", no está claro si el código que está probando depende de comenzar con "a" o terminar con "c" o tener 3 caracteres de largo, etc., etc.