I write the following XUnit test to test a .Net Core WebApi controller's action:
namespace VistaBest.XUnitTest.Api.Test
{
public class Account_UnitTest
{
[Fact]
public void ValidateUserTest()
{
const string username = "admin";
const string password = "admin";
var usersBusinessObjectMock = new Mock<IUsersBusinessObject>();
usersBusinessObjectMock.Setup(service => service.ValidateUser(username, password)).Returns(() => true);
var controller = new AccountController(usersBusinessObjectMock.Object);
var actionResult = controller.ValidateUser(new LoginModel
{
Username = username,
Password = password
});
var okObjectResult = Assert.IsType<OkObjectResult>(actionResult);
var result = okObjectResult.Value as bool?;
Assert.True(result);
}
}
}
AccountController:
namespace VistaBest.Api.Controllers
{
public class AccountController : BaseController
{
private readonly IUsersBusinessObject _usersBusinessObject;
public AccountController(IUsersBusinessObject usersBusinessObject)
{
_usersBusinessObject = usersBusinessObject;
}
[HttpPost]
public IActionResult ValidateUser(LoginModel model)
{
if(!ModelState.IsValid) return BadRequest(ModelState);
return Ok(_usersBusinessObject.ValidateUser(model.Username, model.Password.ToMd5Hash()));
}
}
}
IUsersBusinessObject:
namespace VistaBest.Data.BusinessObjects
{
public interface IUsersBusinessObject
{
bool ValidateUser(string username, string password);
UserModel SelectByUsername(string username);
}
public class UsersBusinessObject : BaseBusinessObject, IUsersBusinessObject
{
public UsersBusinessObject(IDbConnection connection) : base(connection)
{
}
private const string TableName = "Users";
public bool ValidateUser(string username, string password)
{
var query = $"SELECT COUNT(*) FROM [{TableName}] WHERE UserName = @username and Password = @password";
return DbConnection.QueryFirst<int>(query, new { username, password }) == 1;
}
}
As you see I said usersBusinessObjectMock
must return true
:
usersBusinessObjectMock
.Setup(service => service.ValidateUser(username, password))
.Returns(() => true);
But var result = okObjectResult.Value as bool?;
is always false
What's wrong?
After rechecking how the controller calls the validation method I realized that the problem is that you are not configuring the mock correctly.
The controller is calling
_usersBusinessObject.ValidateUser(model.Username, model.Password.ToMd5Hash())
Note the ToMd5Hash()
being called on the password.
You however setup the mock like...
usersBusinessObjectMock
.Setup(service => service.ValidateUser(username, password))
.Returns(() => true);
See the problem?
The mock is expecting the raw password and not its hash, thus wont return true
when passed the hashed password. This results in the action's object result always returning false
because the mock always returns false
. That method was hidden off screen so I did not notice it earlier.
So assuming ToMd5Hash()
is some custom extension method,
you either setup the mock to expect the hashed password to match the method under test...
usersBusinessObjectMock
.Setup(service => service.ValidateUser(username, password.ToMd5Hash()))
.Returns(() => true);
or use the It.IsAny<>()
methods to loosen the expectations of the mock...
usersBusinessObjectMock
.Setup(service => service.ValidateUser(It.IsAny<string>(), It.IsAny<string>()))
.Returns(() => true);
so that is does not matter what values you pass to the mock, it will always return true
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments