Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Moq Setup override

Tags:

c#

moq

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.

like image 586
vivek86 Avatar asked Oct 16 '22 05:10

vivek86


1 Answers

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!

like image 190
Levi Avatar answered Oct 21 '22 00:10

Levi