Подтвердить что ты не робот

Как проверить порядок вызова метода с помощью 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 был удален из моих модульных тестов. Мне просто нужно вручную протестировать его. Реверсирование порядка, хотя и было принято в отдельный тестовый класс, где он был протестирован, поэтому он не все плохо.

Спасибо за ссылку о том, что они ищут. Я надеюсь, что он скоро добавится, очень удобно.

4b9b3361

Ответ 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 потребуется не только обновить исходный код, но и обновить несколько тестов);
  • Больше объектно-ориентированного (вы работаете с объектом, а не с абстрактными объектами типа "последовательность" );
  • В результате: гораздо более удобно.

Спасибо.