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

c# - unit - Cómo configurar un método dos veces para diferentes parámetros con simulacro



verify moq (3)

Moq admite esto fuera de la caja con restricciones de argumento:

mock.Setup(ms => ms.ValidateUser( It.Is<string>(u => u == username), It.Is<string>(p => p == password)) .Returns(new ValidUserContext { Principal = principal }); mock.Setup(ms => ms.ValidateUser( It.Is<string>(u => u != username), It.Is<string>(p => p != password)) .Returns(new ValidUserContext());

Catch-all It.IsAny también funciona, pero el orden es importante:

// general constraint first so that it doesn''t overwrite more specific ones mock.Setup(ms => ms.ValidateUser( It.IsAny<string>(), It.IsAny<string>()) .Returns(new ValidUserContext()); mock.Setup(ms => ms.ValidateUser( It.Is<string>(u => u == username), It.Is<string>(p => p == password)) .Returns(new ValidUserContext { Principal = principal });

Me gustaría configurar un método con Moq dos veces, pero parece que el último anula los anteriores. Aquí está mi configuración inicial:

string username = "foo"; string password = "bar"; var principal = new GenericPrincipal( new GenericIdentity(username), new[] { "Admin" }); var membershipServiceMock = new Mock<IMembershipService>(); membershipServiceMock.Setup(ms => ms.ValidateUser(username, password) ).Returns(new ValidUserContext { Principal = principal });

Esto funciona bien, pero quiero que devuelva el new ValidUserContext() si el nombre de usuario o la contraseña son, en lugar de las variables de username y password , como se new ValidUserContext() anteriormente. Para hacer eso, agregué otra configuración, pero esta vez anula la anterior y siempre la aplica:

membershipServiceMock.Setup(ms => ms.ValidateUser(It.IsAny<string>(), It.IsAny<string>()) ).Returns( new ValidUserContext() );

¿Cuál es la forma más elegante de manejar este tipo de situaciones con Moq?

Editar

Resolví el problema con el siguiente enfoque, pero creo que hay una mejor manera de manejar esto:

var membershipServiceMock = new Mock<IMembershipService>(); membershipServiceMock.Setup(ms => ms.ValidateUser(It.IsAny<string>(), It.IsAny<string>()) ).Returns<string, string>((u, p) => (u == username && p == password) ? new ValidUserContext { Principal = principal } : new ValidUserContext() );


Otra opción lista para usar es usar la versión Return <> para devolver diferentes ValidUserContexts dependiendo de los parámetros. No es mejor que la respuesta anterior, solo otra opción.

Configuramos ValidateUser () para devolver el resultado de una función GetUserContext (cadena, cadena), pasando el nombre de usuario y la contraseña con la que se llamó a ValidateUser ().

[TestClass] public class MultipleReturnValues { public class ValidUserContext { public string Principal { get; set; } } public interface IMembershipService { ValidUserContext ValidateUser(string name, string password); } [TestMethod] public void DifferentPricipals() { var mock = new Mock<IMembershipService>(); mock.Setup(mk => mk.ValidateUser(It.IsAny<string>(), It.IsAny<string>())).Returns<string, string>(GetUserContext); var validUserContext = mock.Object.ValidateUser("abc", "cde"); Assert.IsNull(validUserContext.Principal); validUserContext = mock.Object.ValidateUser("foo", "bar"); Assert.AreEqual(sPrincipal, validUserContext.Principal); } private static string sPrincipal = "A Principal"; private static ValidUserContext GetUserContext(string name, string password) { var ret = new ValidUserContext(); if (name == "foo" && password == "bar") { ret = new ValidUserContext { Principal = sPrincipal }; } return ret; } }


Si nos fijamos en la definición de función para la Setup() : Todo lo que necesita hacer es cambiar el orden de dos llamadas de Setup() :

membershipServiceMock.Setup(ms => ms.ValidateUser(It.IsAny<string>(), It.IsAny<string>()) ).Returns( new ValidUserContext() ); membershipServiceMock.Setup(ms => ms.ValidateUser(username, password) ).Returns(new ValidUserContext { Principal = principal });

por lo tanto, si la entrada es de hecho un username y una password , ambas llamadas de Setup() están calificadas pero luego se ganan debido a la regla y cuando tiene otras entradas, solo la primera coincide y se aplica