Unit testing with Machine.Specifications (aka MSpecs) and FakeItEasy – Continued

In the last post Unit testing with Machine.Specifications (aka MSpecs) and FakeItEasy, I have covered basic unit testing with MSpecs and FakeItEasy. Today first I am going to go through some more tests. And then I will cover the concepts of white-box testing with MSpecs and FakeItEasy.

New tests for positive cases

In the last post, I just covered only the exception test case. Today in this post, I will first start with a positive test case. I will test the scenario where a proper User object is returned.

To achieve that, first of all, I will create a new class to represent the test case.

[Subject(typeof(UserManager))]
public class When_User_Returned
{
}

Next, I will add the code to establish the context and write the assertions.

[Subject(typeof(UserManager))]
public class When_User_Returned
{
   static UserManager Subject;
   static User userResponse;

   Establish context = () => {
       var userProvider = A.Fake();
       A.CallTo(() => userProvider.Get(A._)).Returns(new User { Name = "Test" });
       Subject = new UserManager(userProvider);
   };

   Because of = () => userResponse = Subject.Get(1);

   It should_give_non_null_user = () => userResponse.ShouldNotBeNull();

   It should_have_user_Name_as_Test = () => userResponse.Name.ShouldBeEqualIgnoringCase("Test");
}

Here one thing in particularly notable. The code where I am setting up IUserProvider to return a User object. In this section of the code, I am using A._ as a parameter to the Get method. What this tells us that as long as any Integer parameter is passed as an argument, return a User object with Name = "Test".

Adding a ILogger interface

To support the logging facility, I will add a ILogger interface to the project. This interface will have just one method for logging.

public interface ILogger
{
    void Log(string message);
}

Next, I will pass this interface to the UserManager constructor and use it in the Get method for logging.

public class UserManager : IUserManager
{
    private readonly IUserProvider userProvider;
    private readonly ILogger logger;

    public UserManager(IUserProvider userProvider, ILogger logger)
    {
        this.userProvider = userProvider;
        this.logger = logger;
    }

    public User Get(int userId)
    {
        if(userId <= 0)
        {
            var message = $"User id has to be a positive integer. Value passed {userId}";
            logger.Log(message);
            throw new ArgumentException(message);
        }
        return userProvider.Get(userId);
    }
}

White-box testing

The focus of the white-box testing here would be to see if the ILogger is getting called with a specific argument, when a negative user id is passed.

First of all, I will update the negative test which I wrote in the previous post, to incorporate the ILogger change.

[Subject(typeof(UserManager))]
public class When_User_Id_Passed_As_Negative_Value
{
    static UserManager Subject;
    static Exception Exception;
    static ILogger logger;

    Establish context = () => {
        var userProvider = A.Fake();
        logger = A.Fake();

        Subject = new UserManager(userProvider, logger);
    };

    Because of = () => Exception = Catch.Exception(() => Subject.Get(-1));

    It should_fail = () => Exception.ShouldBeOfExactType();
}

Finally, I will update the code to add code for white-box test and ensure that the ILogger is getting called.

[Subject(typeof(UserManager))]
public class When_User_Id_Passed_As_Negative_Value
{
    static UserManager Subject;
    static Exception Exception;
    static ILogger logger;

    Establish context = () => {
        var userProvider = A.Fake();
        logger = A.Fake();
        Subject = new UserManager(userProvider, logger);
    };

    Because of = () => Exception = Catch.Exception(() => Subject.Get(-1));

    It should_fail = () => Exception.ShouldBeOfExactType();

    It should_log_message = () => A.CallTo(() => logger.Log(A._)).MustHaveHappenedOnceExactly();
}

Conclusion

As we can see, apart from standard test cases, doing white-box testing is also a breeze using MSpecs and FakeItEasy.

The output of the tests: