Reputation: 11340
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?
Upvotes: 0
Views: 312
Reputation: 12956
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.)
Upvotes: 3
Reputation: 14716
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
.
Upvotes: 3