learntogrow-growtolearn
learntogrow-growtolearn

Reputation: 1280

Mock Unit Testing - Getting details of the classes implementing interface

I have a User Interface lets call it IUser. There are two implementations of this: AdminUser and NormalUser.

Now, I am trying to use these user classes via Unit Testing(Mocking).

I mock the interface as follows:

var mockUser = new Mock<IUser>();

mockUser.get("username");

I have added breakkpoints throughout the classes but I am not sure which instance of the interface is getting called i.e AdminUser or NormalUser.

It never stops at the debug points and no clue from the mockUser instance.

How can I get the details of the class being called by the mockUser mock instance?

Thanks in advance.

Upvotes: 2

Views: 1099

Answers (2)

Scott Hannen
Scott Hannen

Reputation: 29207

Creating a Mock<IUser> actually creates a new implementation of IUser. So it won't help you to test any of your actual implementations.

Using a Mock works something like this:

Suppose I have this class and interface. The class validates whether a postal code is valid for a country. It depends on another interface which provides the regex pattern for the given country.

public class PostalCodeValidator
{
    private readonly IPostalCodeRegexProvider _regexProvider;

    public PostalCodeValidator(IPostalCodeRegexProvider regexProvider)
    {
        _regexProvider = regexProvider;
    }

    public bool ValidatePostalCode(string postalCode, string countryCode)
    {
        var regex = _regexProvider.GetPostalCodeRegex(countryCode);
        if (string.IsNullOrEmpty(regex)) return true;
        return Regex.IsMatch(postalCode, regex);
    }
}

public interface IPostalCodeRegexProvider
{
    string GetPostalCodeRegex(string countryCode);
}

The implementation of IPostalCodeRegexProvider could be anything. It could call a database, it could be hard-coded.

But when I write unit tests for PostalCodeValidator, I explicitly don't want to test a real implementation of IPostalCodeRegexProvider. I want to make IPostalCodeValidator return exactly what I want so that I can make sure that PostalCodeValidator works. I'm only testing PostalCodeValidator.

If I want to test that ValidatePostalCode returns true when IPostalCodeRegexProvider.GetPostalCode returns null, then I need to make sure that it will return null. That's where the Mock comes in.

It allows me to easily create an implementation of IPostalCodeRegexProvider that will always return null, so I can test what ValidatePostalCode does with that null.

[TestMethod]
public void ValidatePostalCode_ReturnsTrueWhenRegexIsNull()
{
    var mock = new Mock<IPostalCodeRegexProvider>();
    mock.Setup(x => x.GetPostalCodeRegex(It.IsAny<string>())).Returns(default(string));
    var subject = new PostalCodeValidator(mock.Object);
    Assert.IsTrue(subject.ValidatePostalCode("xyz","abc"));
}

Whatever the subject of the test is - in this case PostalCodeValidator, or in your case AdminUser and NormalUser - that's what you would create an instance of. If those classes depend on other interfaces then you might create a Mock for each of those interfaces.

You can also consider using a "test double." Instead of using Moq, you just create a simple class that implements the interface. For example, what I did with Moq I could also do like this:

public class PostalCodeRegexProviderThatReturnsNull : IPostalCodeRegexProvider
{
    public string GetPostalCodeRegex(string countryCode)
    {
        return null;
    }
}

Now the unit test would look like this:

public void ValidatePostalCode_ReturnsTrueWhenRegexIsNull()
{
    var regexProvider = new PostalCodeRegexProviderThatReturnsNull();
    var subject = new PostalCodeValidator(regexProvider);
    Assert.IsTrue(subject.ValidatePostalCode("xyz","abc"));
}

That is often easier to understand than using a Mock. Sometimes the setup for mocks can get complicated and difficult to read and debug, but a simple class can do the job just as well or even better.

Upvotes: 5

Nkosi
Nkosi

Reputation: 247018

In order to test the actual implementations then you need to initialize the actual implementations i.e. new AdminUser().

For example

[TestMethod]
public void TestAdminUser {
    //Arrange
    IUser admin = new AdminUser();
    //...set any necessary members relevant to the test

    //Act
    //...invoke member to be tested

    //Assert
    //...verify actual to expected behavior

}

If either of the implementations have external dependencies then you would mock those (the dependencies) and inject them.

If a class depended on an IUser

public class SomeClass {
    private readonly IUser user;

    public SomeClass(IUser user) {
        this.user = user;
    }

    //...
}

and you wanted to test that class then you would have a reason to mock IUser for an isolated unit test.

[TestMethod]
public void TestSomeClass {
    //Arrange
    var username = "dummy";
    var expected = "some value";
    var mock = new Mock<IUser>();
    //...set any necessary members relevant to the test
    mock.Setup(_ => _.username).Returns(username);

    var subject = new SomeClass(mock.Object);

    //Act
    //...invoke member to be tested
    var actual = subject.SomeMethod();

    //Assert
    //...verify actual to expected behavior
    Assert.AreEqual(actual, expected);
}

Reference Moq Quickstart to get a better understanding of how to use Moq

Upvotes: 1

Related Questions