Różne wartości zwracane za pierwszym i drugim razem z Moq
Mam taki test:
[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 działa dwa razy w moim dashboardpathresolver, Jak mogę powiedzieć Moq, aby zwrócił null za pierwszym razem i pageModel.Ojbect drugi?
7 answers
W najnowszej wersji Moq (4.2.1312.1622) możesz ustawić sekwencję zdarzeń za pomocą SetupSequence. Oto przykład:
_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);
Wywołanie connect zakończy się sukcesem tylko przy trzeciej i piątej próbie, w przeciwnym razie zostanie wyrzucony wyjątek.
Więc dla Twojego przykładu byłoby to coś w stylu:
repository.SetupSequence(x => x.GetPageByUrl<IPageModel>(virtualUrl))
.Returns(null)
.Returns(pageModel.Object);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-25 18:47:50
Istniejące odpowiedzi są świetne, ale pomyślałem, że dorzucę swoją alternatywę, która po prostu używa {[2] } i nie wymaga żadnej specjalnej wiedzy o szyderczym frameworku-ponieważ nie miałem żadnej,kiedy pisałem! :)
var pageModel = new Mock<IPageModel>();
IPageModel pageModelNull = null;
var pageModels = new Queue<IPageModel>();
pageModels.Enqueue(pageModelNull);
pageModels.Enqueue(pageModel.Object);
Więc...
repository.Setup(x => x.GetPageByUrl<IPageModel>(path)).Returns(pageModels.Dequeue);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-27 22:49:10
Dodanie callback nie działa dla mnie, użyłem tego podejścia zamiast http://haacked.com/archive/2009/09/29/moq-sequences.aspx i skończyłem z takim testem:
[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);
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-13 22:03:56
Możesz użyć wywołania zwrotnego podczas konfigurowania makiety obiektu. Spójrz na przykład z 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());
Twoja konfiguracja może wyglądać tak:
var pageObject = pageModel.Object;
repository.Setup(x => x.GetPageByUrl<IPageModel>(path)).Returns(() => pageObject).Callback(() =>
{
// assign new value for second call
pageObject = new PageModel();
});
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-13 22:03:22
Teraz możesz użyć SetupSequence. Zobacz ten post: http://codecontracts.info/2011/07/28/moq-setupsequence-is-great-for-mocking/
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-22 12:41:20
/ Align = "left" /
Muszę uzyskać różne wartości zwracane przez mocka oparte na różnych wartościach wejściowych i znaleźć rozwiązanie, które IMO jest bardziej czytelne, ponieważ używa deklaratywnej składni 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-26 14:01:17
Akceptowana ODPOWIEDŹ , jak również odpowiedź SetupSequence, obsługuje zwracanie stałych.
Returns()
ma kilka przydatnych przeciążeń, w których można zwrócić wartość na podstawie parametrów, które zostały wysłane do metody mocked. Na podstawie rozwiązania podanego w zaakceptowanej odpowiedzi, oto kolejna metoda rozszerzenia dla tych przeciążeń.
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));
}
}
Niestety, użycie metody wymaga podania pewnych parametrów szablonu, ale wynik jest nadal dość czytelne.
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,
});
Tworzenie przeciążeń dla metody rozszerzenia z wieloma parametrami (T2
, T3
, itp.) w razie potrzeby.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:22