Как проверить порядок вызова метода с помощью Moq
В данный момент у меня есть:
[Test]
public void DrawDrawsAllScreensInTheReverseOrderOfTheStack() {
// Arrange.
var screenMockOne = new Mock<IScreen>();
var screenMockTwo = new Mock<IScreen>();
var screens = new List<IScreen>();
screens.Add(screenMockOne.Object);
screens.Add(screenMockTwo.Object);
var stackOfScreensMock = new Mock<IScreenStack>();
stackOfScreensMock.Setup(s => s.ToArray()).Returns(screens.ToArray());
var screenManager = new ScreenManager(stackOfScreensMock.Object);
// Act.
screenManager.Draw(new Mock<GameTime>().Object);
// Assert.
screenMockOne.Verify(smo => smo.Draw(It.IsAny<GameTime>()), Times.Once(),
"Draw was not called on screen mock one");
screenMockTwo.Verify(smo => smo.Draw(It.IsAny<GameTime>()), Times.Once(),
"Draw was not called on screen mock two");
}
Но порядок, в котором я рисую объекты в производственном коде, не имеет значения. Я мог бы сделать сначала, или два, это неважно. Однако это должно иметь значение, поскольку порядок вытягивания важен.
Как вы (используя Moq) гарантируете, что методы вызываются в определенном порядке?
Edit
Я избавился от этого теста. Метод draw был удален из моих модульных тестов. Мне просто нужно вручную протестировать его. Реверсирование порядка, хотя и было принято в отдельный тестовый класс, где он был протестирован, поэтому он не все плохо.
Спасибо за ссылку о том, что они ищут. Я надеюсь, что он скоро добавится, очень удобно.
Ответы
Ответ 1
Похоже, что он в настоящее время не реализован. См. Проблема 24: MockSequence. Этот поток обсуждает проблему.
Однако вы можете пересмотреть свои тесты. Обычно я чувствую, что заказ на тестирование приводит к хрупким испытаниям, поскольку он часто тестирует детали реализации.
EDIT: Я не уверен, что это касается вопроса OP. Ответ Лусеро может быть более полезным.
Ответ 2
Недавно я создал Moq.Sequences, который обеспечивает возможность проверки порядка в Moq. Вы можете прочитать мой post, который описывает следующее:
- Поддерживает вызовы методов, свойство
сеттеры и геттеры.
- Позволяет указать количество
раз конкретный вызов должен быть
ожидается.
- Предоставляет петли, которые позволяют
групповые вызовы в повторяющуюся группу.
- Позволяет указать номер
из циклов следует ожидать цикла.
- Звонки, которые, как ожидается, будут вызваны
в последовательности может быть смешанно с
которые ожидаются в любом порядке.
- Поддержка многопоточности.
Типичное использование выглядит следующим образом:
[Test]
public void Should_show_each_post_with_most_recent_first_using_sequences()
{
var olderPost = new Post { DateTime = new DateTime(2010, 1, 1) };
var newerPost = new Post { DateTime = new DateTime(2010, 1, 2) };
var posts = new List<Post> { newerPost, olderPost };
var mockView = new Mock<BlogView>();
using (Sequence.Create())
{
mockView.Setup(v => v.ShowPost(newerPost)).InSequence();
mockView.Setup(v => v.ShowPost(olderPost)).InSequence();
new BlogPresenter(mockView.Object).Show(posts);
}
}
Ответ 3
Простое решение с использованием Moq CallBacks:
[TestMethod]
public void CallInOrder()
{
// Arrange
string callOrder = "";
var service = new Mock<MyService>();
service.Setup(p=>p.FirstCall()).Returns(0).CallBack(()=>callOrder += "1");
service.Setup(p=>p.SecondCall()).Returns(0).CallBack(()=>callOrder += "2");
var sut = new Client(service);
// Act
sut.DoStuff();
// Assert
Assert.AreEqual("12", callOrder);
}
Ответ 4
Посмотрите на это сообщение в блоге, оно может решить вашу проблему.
Ответ 5
В противном случае вы могли бы использовать функции обратного вызова и увеличивать/сохранять значение callIndex.
Ответ 6
Из исходного сообщения я мог предположить, что метод тестирования выполняет следующие операции:
var screenOne = new Screen(...);
var screenTwo = new Screen(...);
var screens = new []{screenOne, screenTwo};
var screenManager = new ScreenManager(screens);
screenManager.Draw();
Реализация метода "Draw" выглядит примерно так:
public class ScreenManager
{
public void Draw()
{
_screens[0].Draw();
_screens[1].Draw();
}
}
С моей точки зрения, если порядок вызовов очень важен, в систему должна быть добавлена дополнительная структура (описывающая последовательность).
Простейшая реализация: каждый экран должен знать свой последующий элемент и вызывать его метод Draw после рисования:
// 1st version
public class Screen(Screen screenSubSequent)
{
private Screen _screenNext;
public Screen(Screen screenNext)
{
_screenNext=screenNext;
}
public void Draw()
{
// draw himself
if ( _screenNext!=null ) _screenNext.Draw();
}
}
public class ScreenManager
{
public void Draw()
{
_screens[0].Draw();
}
}
static void Main()
{
var screenOne = new Screen(null, ...);
var screenTwo = new Screen(screenOne, ...);
var screens = new []{screenOne, screenTwo};
var screenManager = new ScreenManager(screens);
}
С одной точки каждый элемент экрана должен знать немного о другом. Это не всегда хорошо. Если это так: вы можете создать такой класс, как "ScreenDrawer". Этот объект будет хранить собственный экран и последующий экран (возможно, наследует его от класса Screen.Используя другие миры: класс ScreenDrawer описывает структуру системы. Вот простейший сценарий реализации:
// 2nd version
public class ScreenDrawer
{
private Screen _screenNext;
public ScreenDrawer(Screen screenNext, ...) : base (...)
{
_screenNext=screenNext;
}
public void Draw()
{
// draw himself
if ( _screenNext!=null ) _screenNext.Draw();
}
}
public class ScreenManager
{
public void Draw()
{
_screens[0].Draw();
}
}
static void Main()
{
var screenOne = new ScreenDrawer(null, ...);
var screenTwo = new ScreenDrawer(screenOne, ...);
var screens = new []{screenOne, screenTwo};
var screenManager = new ScreenManager(screens);
}
Второй метод вводит дополнительное наследование, но не требует, чтобы класс Screen знал о его элементе подпоследовательности.
Резюме: оба метода выполняют подпоследовательные вызовы и не требуют тестирования "последовательности". Вместо этого они требуют тестирования, если текущий "экран" вызывает другой, и тестирование, если "ScreenManager" вызывает метод "Draw" первого элемента в последовательности.
Этот подход:
- Более тестируемое (может быть реализовано с использованием большей части тестовой среды без необходимости поддержки "тестирования последовательности" );
- Более стабильный (никто не может легко изменить последовательность: hi потребуется не только обновить исходный код, но и обновить несколько тестов);
- Больше объектно-ориентированного (вы работаете с объектом, а не с абстрактными объектами типа "последовательность" );
- В результате: гораздо более удобно.
Спасибо.