Question

I'm very new to unit testing and TDD on a whole.

I have the following Login method in my Service layer (WCF) which is hosted as a windows service. My service layer follows the facade pattern, and all calls to the service layer are made with requests objects and returns the corresponding response object.

public LoginResponse Login(LoginRequest request)
{
    var response = new LoginResponse(request.RequestId);
    try
    {
        if (!ValidRequest(request, response, Validate.ClientTag))
            return response;

        var user = userDao.GetByUserId(request.UserId);
        if (user != null)
        {                   
            if (request.Password != "")
            {
                var authenticationService = new AuthenticationService();
                if (authenticationService.Authenticate(user, request.Password))
                {                          
                    return response;
                }                        
                response.ErrorCode = "IncorrectPassword";
            }
            else
            {                       
                response.ErrorCode = "PasswordNotFound";
            }
        }
        else
        {
            response.ErrorCode = "UserNotFound";
        }
        response.Acknowledge = AcknowledgeType.Failure;
        return response;
    }
    catch (Exception ex)
    {
        Log.Error(ex);
        response.Acknowledge = AcknowledgeType.Failure;
        response.ErrorCode = "Exception";
        return response;
    }
}

Both lines here:

userDao.GetByUserId(request.UserId);

and

authenticationService.Authenticate(user, request.Password);

are making calls to the DB.

Here's the test I've written with xUnit and JustMock:

    [Theory]
    [InlineData("manager", "manager")]
    public void LoginTest(string userId, string password)
    {
        //Arrange
        var userServiceMock = Mock.Create<IUserManagementService>();

        var request = new LoginRequest().Prepare();
        request.UserId = userId;
        request.Password = password;

        var response = new LoginResponse(request.RequestId);

        Mock.Arrange(() => userServiceMock.Login(request)).Returns(response).OccursOnce();

        //Act
        userServiceMock.Login(request);

        //Assert
        Mock.Assert(userServiceMock);            
    }

The problem I'm having is even after I've changed my service method to

public LoginResponse Login(LoginRequest request)
{
    return null;
}

My test still passes. What am I doing wrong?

Was it helpful?

Solution

You are not testing anything. You are mocking your system under test which is not correct.

See this answer for a brief explanation of what a mock is.

If you are testing UserManagementService.Login() you'd want:

[Theory]
[InlineData("manager", "manager")]
public void LoginTest(string userId, string password)
{
    // Arrange
    // System under test
    IUserManagementService userService = new UserManagementService();

    var request = new LoginRequest().Prepare();
    request.UserId = userId;
    request.Password = password;

    var expectedResponse = new LoginResponse(request.RequestId);

    //Act
    var actualResponse = userService.Login(request);

    //Assert
    Assert.AreEqual(actualResponse.Something, expectedResponse.Something);            
}

No need to mock anything. (You may need to mock any dependencies that Login() has such as AuthenticationService, userDao and even Log if you don't want your unit test to write to the log.)

OTHER TIPS

You have mocked the class that you want to test. When you call Login on the userServiceMock, the following line of code is relevant:

Mock.Arrange(() => userServiceMock.Login(request)).Returns(response).OccursOnce();

That is, you always return a valid result. The actual code is never executed.

Each of your unit tests will have a "system under test" (SUT). You will never want to mock the SUT itself. You only mock any other objects that your SUT depends on. In your case, the UserService seems to be your SUT. There is no reason to mock the user service, you want to test the real one!

However the SUT seems to use a UserDao which is probably injected somewhere. Inject a mock of the UserDAO instead of a real one.

Furthermore it uses a AuthenticationService. Unfortunately it is not injected but your method creates an instance on the fly. This makes it impossible to use a mock implementation of it. You will need to refactor your UserService such that you can inject the AuthenticationService to be used. Then your unit test can inject a mock of the AuthenticationService.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top