Valores de retorno diferentes la primera y segunda vez con Moq
Tengo una prueba como esta:
[TestCase("~/page/myaction")]
public void Page_With_Custom_Action(string path) {
// Arrange
var pathData = new Mock<IPathData>();
var pageModel = new Mock<IPageModel>();
var repository = new Mock<IPageRepository>();
var mapper = new Mock<IControllerMapper>();
var container = new Mock<IContainer>();
container.Setup(x => x.GetInstance<IPageRepository>()).Returns(repository.Object);
repository.Setup(x => x.GetPageByUrl<IPageModel>(path)).Returns(() => pageModel.Object);
pathData.Setup(x => x.Action).Returns("myaction");
pathData.Setup(x => x.Controller).Returns("page");
var resolver = new DashboardPathResolver(pathData.Object, repository.Object, mapper.Object, container.Object);
// Act
var data = resolver.ResolvePath(path);
// Assert
Assert.NotNull(data);
Assert.AreEqual("myaction", data.Action);
Assert.AreEqual("page", data.Controller);
}
GetPageByUrl
se ejecuta dos veces en mi DashboardPathResolver
, ¿cómo puedo decirle a Moq que regrese null
la primera vez y pageModel.Object
la segunda?
Con la última versión de Moq (4.2.1312.1622), puede configurar una secuencia de eventos usando SetupSequence . He aquí un ejemplo:
_mockClient.SetupSequence(m => m.Connect(It.IsAny<String>(), It.IsAny<int>(), It.IsAny<int>()))
.Throws(new SocketException())
.Throws(new SocketException())
.Returns(true)
.Throws(new SocketException())
.Returns(true);
Llamar a connect solo será exitoso en el tercer y quinto intento; de lo contrario, se generará una excepción.
Entonces, para tu ejemplo, sería algo como:
repository.SetupSequence(x => x.GetPageByUrl<IPageModel>(virtualUrl))
.Returns(null)
.Returns(pageModel.Object);
Las respuestas existentes son geniales, pero pensé en incluir mi alternativa que solo usa System.Collections.Generic.Queue
y no requiere ningún conocimiento especial del marco simulado, ¡ya que no tenía ninguno cuando lo escribí! :)
var pageModel = new Mock<IPageModel>();
IPageModel pageModelNull = null;
var pageModels = new Queue<IPageModel>();
pageModels.Enqueue(pageModelNull);
pageModels.Enqueue(pageModel.Object);
Entonces...
repository.Setup(x => x.GetPageByUrl<IPageModel>(path)).Returns(pageModels.Dequeue);
Ahora puedes usar SetupSequence. Ver esta publicación .
var mock = new Mock<IFoo>();
mock.SetupSequence(f => f.GetCount())
.Returns(3) // will be returned on 1st invocation
.Returns(2) // will be returned on 2nd invocation
.Returns(1) // will be returned on 3rd invocation
.Returns(0) // will be returned on 4th invocation
.Throws(new InvalidOperationException()); // will be thrown on 5th invocation
Puede utilizar una devolución de llamada al configurar su objeto simulado. Eche un vistazo al ejemplo de Moq Wiki ( https://github.com/Moq/moq4/wiki/Quickstart ).
// returning different values on each invocation
var mock = new Mock<IFoo>();
var calls = 0;
mock.Setup(foo => foo.GetCountThing())
.Returns(() => calls)
.Callback(() => calls++);
// returns 0 on first invocation, 1 on the next, and so on
Console.WriteLine(mock.Object.GetCountThing());
Su configuración podría verse así:
var pageObject = pageModel.Object;
repository.Setup(x => x.GetPageByUrl<IPageModel>(path)).Returns(() => pageObject).Callback(() =>
{
// assign new value for second call
pageObject = new PageModel();
});
Agregar una devolución de llamada no funcionó para mí, usé este enfoque en su lugar http://haacked.com/archive/2009/09/29/moq-sequences.aspx y terminé con una prueba como esta:
[TestCase("~/page/myaction")]
[TestCase("~/page/myaction/")]
public void Page_With_Custom_Action(string virtualUrl) {
// Arrange
var pathData = new Mock<IPathData>();
var pageModel = new Mock<IPageModel>();
var repository = new Mock<IPageRepository>();
var mapper = new Mock<IControllerMapper>();
var container = new Mock<IContainer>();
container.Setup(x => x.GetInstance<IPageRepository>()).Returns(repository.Object);
repository.Setup(x => x.GetPageByUrl<IPageModel>(virtualUrl)).ReturnsInOrder(null, pageModel.Object);
pathData.Setup(x => x.Action).Returns("myaction");
pathData.Setup(x => x.Controller).Returns("page");
var resolver = new DashboardPathResolver(pathData.Object, repository.Object, mapper.Object, container.Object);
// Act
var data = resolver.ResolvePath(virtualUrl);
// Assert
Assert.NotNull(data);
Assert.AreEqual("myaction", data.Action);
Assert.AreEqual("page", data.Controller);
}