IT이야기

Moq를 사용하여 전화가 올바른 순서로 이루어졌는지 확인

cyworld 2021. 9. 24. 21:36
반응형

Moq를 사용하여 전화가 올바른 순서로 이루어졌는지 확인


다음 방법을 테스트해야 합니다.

CreateOutput(IWriter writer)
{
    writer.Write(type);
    writer.Write(id);
    writer.Write(sender);

    // many more Write()s...
}

저는 Moq'd를 만들었고 메서드가 올바른 순서로 호출 IWriter되었는지 확인하고 싶습니다 Write().

다음 테스트 코드가 있습니다.

var mockWriter = new Mock<IWriter>(MockBehavior.Strict);
var sequence = new MockSequence();
mockWriter.InSequence(sequence).Setup(x => x.Write(expectedType));
mockWriter.InSequence(sequence).Setup(x => x.Write(expectedId));
mockWriter.InSequence(sequence).Setup(x => x.Write(expectedSender));

그러나 ( 을 쓰기 위해) Write()in에 대한 두 번째 호출은 " IWriter.Write() 호출이 모의 동작 Strict로 실패했습니다. 모의에 대한 모든 호출에는 해당 설정이 있어야 합니다. "라는 메시지가 표시됩니다.CreateOutput()idMockException

또한 Moq 시퀀스의 최종 문서/예제를 찾기가 어렵습니다.

제가 뭔가 잘못하고 있는 건가요, 아니면 같은 방법으로 시퀀스를 설정할 수 없나요? 그렇지 않은 경우 사용할 수 있는 대안이 있습니까(바람직하게는 Moq/NUnit 사용)?


동일한 mock 에서 MockSequence 를 사용할 때 버그가 있습니다 . Moq 라이브러리의 이후 릴리스에서 확실히 수정될 것입니다(Moq.MethodCall.Matches 구현을 변경하여 수동으로 수정할 수도 있습니다).

Moq만 사용하려면 콜백을 통해 메서드 호출 순서를 확인할 수 있습니다.

int callOrder = 0;
writerMock.Setup(x => x.Write(expectedType)).Callback(() => Assert.That(callOrder++, Is.EqualTo(0)));
writerMock.Setup(x => x.Write(expectedId)).Callback(() => Assert.That(callOrder++, Is.EqualTo(1)));
writerMock.Setup(x => x.Write(expectedSender)).Callback(() => Assert.That(callOrder++, Is.EqualTo(2)));

원하는 동작을 얻을 수 있었지만 http://dpwhelan.com/blog/software-development/moq-sequences/ 에서 타사 라이브러리를 다운로드해야 합니다.

그런 다음 다음을 사용하여 시퀀스를 테스트할 수 있습니다.

var mockWriter = new Mock<IWriter>(MockBehavior.Strict);
using (Sequence.Create())
{
    mockWriter.Setup(x => x.Write(expectedType)).InSequence();
    mockWriter.Setup(x => x.Write(expectedId)).InSequence();
    mockWriter.Setup(x => x.Write(expectedSender)).InSequence();
}

이 솔루션을 문서화하는 데 도움이 되도록 이 답변을 부분적으로 추가했지만 Moq 4.0만 사용하여 비슷한 결과를 얻을 수 있는지 여부에 여전히 관심이 있습니다.

Moq가 아직 개발 중인지 확실하지 않지만 로 문제를 수정 MockSequence하거나 Moq에 moq-sequences 확장을 포함하는 것이 좋습니다.


호출 순서에 따라 주장할 확장 메서드를 작성했습니다.

public static class MockExtensions
{
  public static void ExpectsInOrder<T>(this Mock<T> mock, params Expression<Action<T>>[] expressions) where T : class
  {
    // All closures have the same instance of sharedCallCount
    var sharedCallCount = 0;
    for (var i = 0; i < expressions.Length; i++)
    {
      // Each closure has it's own instance of expectedCallCount
      var expectedCallCount = i;
      mock.Setup(expressions[i]).Callback(
        () =>
          {
            Assert.AreEqual(expectedCallCount, sharedCallCount);
            sharedCallCount++;
          });
    }
  }
}

클로저가 범위 변수와 관련하여 작동하는 방식을 활용하여 작동합니다. sharedCallCount에 대한 선언이 하나만 있기 때문에 모든 클로저에는 동일한 변수에 대한 참조가 있습니다. expectedCallCount를 사용하면 루프가 반복될 때마다 새 인스턴스가 인스턴스화됩니다(단순히 클로저에서 i를 사용하는 것과 반대). 이런 식으로 각 클로저는 표현식이 호출될 때 sharedCallCount와 비교하기 위해 자체 범위의 i 복사본을 갖습니다.

다음은 확장에 대한 작은 단위 테스트입니다. 이 메서드는 어설션 섹션이 아니라 설정 섹션에서 호출됩니다.

[TestFixture]
public class MockExtensionsTest
{
  [TestCase]
  {
    // Setup
    var mock = new Mock<IAmAnInterface>();
    mock.ExpectsInOrder(
      x => x.MyMethod("1"),
      x => x.MyMethod("2"));

    // Fake the object being called in order
    mock.Object.MyMethod("1");
    mock.Object.MyMethod("2");
  }

  [TestCase]
  {
    // Setup
    var mock = new Mock<IAmAnInterface>();
    mock.ExpectsInOrder(
      x => x.MyMethod("1"),
      x => x.MyMethod("2"));

    // Fake the object being called out of order
    Assert.Throws<AssertionException>(() => mock.Object.MyMethod("2"));
  }
}

public interface IAmAnInterface
{
  void MyMethod(string param);
}

Recently, I put together two features for Moq: VerifyInSequence() and VerifyNotInSequence(). They work even with Loose Mocks. However, these are only available in a moq repository fork:

https://github.com/grzesiek-galezowski/moq4

and await more comments and testing before deciding on whether they can be included in official moq releaase. However, nothing prevents you from downloading the source as ZIP, building it into a dll and giving it a try. Using these features, the sequence verification you need could be written as such:

var mockWriter = new Mock<IWriter>() { CallSequence = new LooseSequence() };

//perform the necessary calls

mockWriter.VerifyInSequence(x => x.Write(expectedType));
mockWriter.VerifyInSequence(x => x.Write(expectedId));
mockWriter.VerifyInSequence(x => x.Write(expectedSender));

(note that you can use two other sequences, depending on your needs. Loose sequence will allow any calls between the ones you want to verify. StrictSequence will not allow this and StrictAnytimeSequence is like StrictSequence (no method calls between verified calls), but allows the sequence to be preceeded by any number of arbitrary calls.

If you decide to give this experimental feature a try, please comment with your thoughts on: https://github.com/Moq/moq4/issues/21

Thanks!


The simplest solution would be using a Queue:

var expectedParameters = new Queue<string>(new[]{expectedType,expectedId,expectedSender});
mockWriter.Setup(x => x.Write(expectedType))
          .Callback((string s) => Assert.AreEqual(expectedParameters.Dequeue(), s));

I suspect that expectedId is not what you expect.

However i'd probably just write my own implementation of IWriter to verify in this case ... probably a lot easier (and easier to change later).

Sorry for no Moq advice directly. I love it, but haven't done this in it.

do you maybe need to add .Verify() at the end of each setup? (That really is a guess though i'm afraid).

ReferenceURL : https://stackoverflow.com/questions/10602264/using-moq-to-verify-calls-are-made-in-the-correct-order

반응형