Различные возвращаемые значения первый и второй раз с Moq
У меня есть тест такой:
[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 работает дважды в моем dashboardpathresolver, как я могу сказать Moq, чтобы вернуть null в первый раз и pageModel.Ojbect второй?
7 ответов:
С последней версией Moq (4.2.1312.1622), вы можете настроить последовательность событий с помощью SetupSequence. Вот пример:
_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);
вызов connect будет успешным только с третьей и пятой попытки, в противном случае будет выдано исключение.
Так что для вашего примера это будет что-то вроде:
repository.SetupSequence(x => x.GetPageByUrl<IPageModel>(virtualUrl)) .Returns(null) .Returns(pageModel.Object);
существующие ответы великолепны, но я думал, что брошу свою альтернативу, которая просто использует
System.Collections.Generic.Queue
и не требует никаких специальных знаний о насмешливой структуре-так как у меня их не было, когда я ее писал! :)var pageModel = new Mock<IPageModel>(); IPageModel pageModelNull = null; var pageModels = new Queue<IPageModel>(); pageModels.Enqueue(pageModelNull); pageModels.Enqueue(pageModel.Object);
затем...
repository.Setup(x => x.GetPageByUrl<IPageModel>(path)).Returns(pageModels.Dequeue);
добавление обратного вызова не работает для меня, я использовал этот подход вместо http://haacked.com/archive/2009/09/29/moq-sequences.aspx и я закончил с тестом, как это:
[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); }
Вы можете использовать обратный вызов при настройке макет объекта. Взгляните на пример из Moq Wiki (http://code.google.com/p/moq/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());
настройки может выглядеть так:
var pageObject = pageModel.Object; repository.Setup(x => x.GetPageByUrl<IPageModel>(path)).Returns(() => pageObject).Callback(() => { // assign new value for second call pageObject = new PageModel(); });
теперь вы можете использовать SetupSequence. См. этот пост: http://codecontracts.info/2011/07/28/moq-setupsequence-is-great-for-mocking/
достиг здесь для такого же рода проблемы с несколько иным требованием.
Мне нужно получить различные возвращаемые значения из макета на основе различных входных значений и нашел решение, которое IMO более читабельно, поскольку он использует декларативный синтаксис Moq (linq to Mocks).public interface IDataAccess { DbValue GetFromDb(int accountId); } var dataAccessMock = Mock.Of<IDataAccess> (da => da.GetFromDb(It.Is<int>(acctId => acctId == 0)) == new Account { AccountStatus = AccountStatus.None } && da.GetFromDb(It.Is<int>(acctId => acctId == 1)) == new DbValue { AccountStatus = AccountStatus.InActive } && da.GetFromDb(It.Is<int>(acctId => acctId == 2)) == new DbValue { AccountStatus = AccountStatus.Deleted }); var result1 = dataAccessMock.GetFromDb(0); // returns DbValue of "None" AccountStatus var result2 = dataAccessMock.GetFromDb(1); // returns DbValue of "InActive" AccountStatus var result3 = dataAccessMock.GetFromDb(2); // returns DbValue of "Deleted" AccountStatus
The принято отвечать, а также SetupSequence ответ, обрабатывает возврат константы.
Returns()
имеет некоторые полезные перегрузки, где вы можете вернуть значение на основе параметров, которые были отправлены в издевались метод. На основе решение учитывая принятый ответ, вот еще один метод расширения для этих перегрузок.public static class MoqExtensions { public static IReturnsResult<TMock> ReturnsInOrder<TMock, TResult, T1>(this ISetup<TMock, TResult> setup, params Func<T1, TResult>[] valueFunctions) where TMock : class { var queue = new Queue<Func<T1, TResult>>(valueFunctions); return setup.Returns<T1>(arg => queue.Dequeue()(arg)); } }
к сожалению, использование метода требует указания некоторого шаблона параметры, но результат все равно вполне читаем.
repository .Setup(x => x.GetPageByUrl<IPageModel>(path)) .ReturnsInOrder(new Func<string, IPageModel>[] { p => null, // Here, the return value can depend on the path parameter p => pageModel.Object, });
создать перегрузки для метода расширения с несколькими параметрами (
T2
,T3
и т. д.) При необходимости.