1

Assume that I have the following function "foo" which I want to unit test:

class FooClass
{
    IBar _bar; 
    
    Foo(IBar bar)
    {
        _bar = bar;
    }
    
    void foo()
    {
        byte[] someBytes;
        someBytes = _bar.ReadBytes();
        
        if (someBytes != Expectation1)
        {
            throw new Exception("Byte stream doesn't match expectations");
        }

        someBytes = _bar.ReadBytes();
        
        if (someBytes != Expectation2)
        {
            throw new Exception("Byte stream doesn't match expectations");
        }
        ...
    }
}

"_bar.ReadBytes" is going to read some data expecting specific byte streams that are going to be evaluated in foo after each "ReadBytes"-call.

Now, when calling "foo" in the unit test, how can I influence the return value of "ReadBytes" each time in a specific way? How do I set up the mock?

So for testing I would need to do something like...

public class FooClassTests
{
    [Fact]
    public void Test_Foo()
    {
        Mock<IBar> mockBar = new Mock<IBar>();
        mockComInterface.Setup(mock => mock.ReadBytes()).Returns(new byte[] {}); // returns empty byte array
        
        FooClass fooObject = new FooClass();
        fooObject.foo();

        Assert.something...
    }
}
RadioMan85
  • 337
  • 1
  • 11
  • 1
    I guess you cannot achieve this mock behaviour using Moq. However you might create fake implementation of `IBar` where you define the required behaviour. – Andrei Khotko Mar 16 '21 at 09:41
  • In addition, the method `IBar.ReadBytes()` is not a clean function, so the behaviour of the `FooClass.foo()` method depends on the implementation of `IBar`. In my opinion, you shouldn't inject `IBar` into `FooClass`. Just pass to `foo()` method all needed bytes as parameter. – Andrei Khotko Mar 16 '21 at 09:47
  • Yes and no. Actually I would go with you. The reason why it is injected is because in the real application "_bar" is used in many other functions as well. So to avoid an additional function parameter in all further functions using "_bar" it is injected when being instantiated. Or would you rather pass it as a parameter every time? – RadioMan85 Mar 16 '21 at 10:42

1 Answers1

3

Looks like SetupSequence is what you are looking for

//...

Mock<IBar> mockBar = new Mock<IBar>();
mockBar.SetupSequence(mock => mock.ReadBytes())
    .Returns(expectation1ByteArray)  // will be returned on 1st invocation
    .Returns(expectation2ByteArray); // will be returned on 2nd invocation

FooClass fooObject = new FooClass(mockBar.Object);

//...

Reference Moq Quickstart: sequential calls

Nkosi
  • 235,767
  • 35
  • 427
  • 472