10

I have gone through all the previous answers and none of them solve my problem.

lets say that i have the following code:

public interface ISomeInterface
{
    int SomeMethod(int a, string b);
}

Now i have a common mocking class that defines some default behaviour for the above method

public class CommonMock
{
    public Mock<ISomeInterface> MockInterface = new Mock<ISomeInterface>().Setup(x => x.SomeMethod(It.IsAny<int>(), It.IsAny<string>())).Returns(It.IsAny<int>());
}

I need some default behaviour because I have a whole lot of test cases which need a default behaviour.

But in some specific test scenarios, in a totally separate test class, i need to be able to return a different value as i am testing some specific test case.

Something like below:

[Test]
public void TestSomeMethodSpecific()
{
    var commonMock = new CommonMock();
    commonMock.MockInterface.Setup(x => x.SomeMethod(It.IsAny<int>(), It.IsAny<string>())).Returns(42);

    // Do some test based on the new return value
}

How can I achieve that?

Below i am attaching a bit of the actual code:

Common Setup

public class MockStore
{
    public Mock<IProcessHandler> ProcessHandler = new Mock<IProcessHandler>();
    ProcessHandler.Setup(x => x.GetCurrentProcessRunId()).Returns(It.IsAny<int>());
}

Overridden Setup in a test class

var mockstore = new MockStore();
mockStore.ProcessHandler.Setup(x => x.GetCurrentProcessRunId()).Returns(25);

And there are almost 50 to 70 such mocks each of which return from simple types to complex classes.

vivek86
  • 707
  • 9
  • 18
  • As workaround, you can implement something like MoqValueProvider, which returns common value for most cases and specific value for specific cases and get this value in Moq setup – Pavel Anikhouski Mar 28 '19 at 09:48
  • You can use `TestInitialize` method. – fhnaseer Mar 28 '19 at 09:51
  • Just create default mock within the SetUp method before every test and then within the test you are free to change what ever you want it will be limited to the scope of that test... – Johnny Mar 28 '19 at 12:32
  • Unfortunately, thats not a feasible solution as there are about 2000+ tests on the production env. which is why the default mock behaviour was implemented. – vivek86 Mar 28 '19 at 12:35
  • @vivek86 I don't understand you... Default implementation will be in the `SetUp` method you have it for free within every test you could then only override what ever you need because `moq` works on that principle, last setup wins... – Johnny Mar 28 '19 at 12:39
  • Hi @Johnny: unfortunately, for some reason, the recent setup isnt overriding the previous setup and i cannot put a finger on it. the previous setup always seems to be the one taken in the test, irrespective of what i setup in the test. – vivek86 Mar 28 '19 at 15:00

2 Answers2

4

That should work? If you create a subsequent setup on a method and it's non-conditional (no constraints on the arguments) then it removes all previous setups for the method.

You can see my answer here that explains it with the source code.

If you want multiple Setups that are conditional, to return different values based on the arguments, then see How to setup a method twice for different parameters with mock.

Without seeing your full code, perhaps you are already using conditional Setups. In which case the order is important and perhaps you are overriding an earlier Setup with a more general one.

Owen Pauling
  • 11,349
  • 20
  • 53
  • 64
2

You could have global mock objects that you modify as you go. For instance I have this:

[TestClass]
public class StoreServiceTest
{
    Mock<IAccess> mockAccess;
    Mock<IAccess> mockAccessNoData;
    Mock<IDataReader> mockReader;
    Mock<IDataReader> mockReaderNoData;
    Mock<IStoreService> mockStoreService;

And then on the TestInitiailize, I Setup the default implementation as follows:

mockReader = new Mock<IDataReader>();
mockReader.Setup(m => m.IsDBNull(It.IsAny<int>())).Returns(false);
mockReader.Setup(m => m.GetString(It.IsAny<int>())).Returns("stub");
mockReader.Setup(m => m.GetBoolean(It.IsAny<int>())).Returns(true);
mockReader.Setup(m => m.GetInt32(It.IsAny<int>())).Returns(32);
mockReader.SetupSequence(m => m.Read()).Returns(true).Returns(false); // setup sequence to avoid infinite loop

mockAccess = new Mock<IAccess>();
mockAccess.Setup(m => m.ReadData(It.IsAny<string>(), It.IsAny<object[]>())).Returns(mockReader.Object);

mockReaderNoData = new Mock<IDataReader>();
mockReaderNoData.Setup(m => m.Read()).Returns(false);

mockAccessNoData = new Mock<IAccess>();
mockAccessNoData.Setup(m => m.ReadData(It.IsAny<string>(), It.IsAny<object[]>())).Returns(mockReaderNoData.Object);

mockStoreService = new Mock<IStoreService>(); 

And now for a default kind of test, all I do is pass the mockReader.Object which should have the default implementation since every test begins with TestInitialize, and then for a special case, say I want to return "sub" instead of "stub" for IDataReader's GetString() method, I can do something like this:

mockReader.Setup(m => m.GetString(It.IsAny<int>())).Returns((int col) => {
    if (col == 2) return "sub";
    else return "stub";
});

Hope that helps!

Levi
  • 321
  • 2
  • 12