我编写了以下XUnit测试来测试.Net Core WebApi控制器的操作:
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;
}
}
如您所见,我说usersBusinessObjectMock
必须返回true
:
usersBusinessObjectMock
.Setup(service => service.ValidateUser(username, password))
.Returns(() => true);
但var result = okObjectResult.Value as bool?;
总是false
怎么了?
答案 0 :(得分:6)
读完10分钟后的代码,因为这个错误很难找到。你做的一切都很正确......
var okObjectResult = Assert.IsType<OkObjectResult>(actionResult);
var result = okObjectResult.Value as bool?;
Assert.True(result);
但这是错误...
var很危险...... 你的变量okObjectResult不是来自OkObjectResult类型......这就是为什么你的断言从来都不是真的...... 我确定你的意思是键入
Assert.IsType<OkObjectResult>(actionResult);
var okObjectResult = (OkObjectResult) actionResult;
答案 1 :(得分:3)
这会有效,我会在你的代码中制作一些小的重构。
namespace VistaBest.XUnitTest.Api.Test
{
public class Account_UnitTest
{
private readonly Mock<IUsersBusinessObject> _usersBusinessObjectMock;
private readonly AccountController _accountController;
public Account_UnitTest()
{
_usersBusinessObjectMock = new Mock<IUsersBusinessObject>();
_accountController = new AccountController(_usersBusinessObjectMock.Object);
}
[Fact]
public void ValidateUserTest()
{
var model = new LoginModel
{
Username = "admin",
Password = "admin"
};
_usersBusinessObjectMock.Setup(service => service.ValidateUser(model.Username, model.Password)).Returns(() => true);
var actual = _accountController.ValidateUser(model) as OkObjectResult;
actual.Value.ShouldBeEquivalentTo(true);
// or Assert.True(actual.Value);
}
}
}
答案 2 :(得分:1)
在重新检查控制器如何调用验证方法后,我意识到问题在于您没有正确配置模拟。
控制器正在调用
_usersBusinessObject.ValidateUser(model.Username, model.Password.ToMd5Hash())
请注意密码调用ToMd5Hash()
。
然而,您可以将模拟设置为......
usersBusinessObjectMock
.Setup(service => service.ValidateUser(username, password))
.Returns(() => true);
看到问题?
模拟期望原始密码而不是其哈希值,因此在传递哈希密码时不会返回true
。这会导致操作的对象结果始终返回false
,因为模拟始终返回false
。该方法隐藏在屏幕之外,所以我之前没有注意到它。
因此假设ToMd5Hash()
是一些自定义扩展方法,
您要么设置模拟以期望散列密码与测试中的方法匹配...
usersBusinessObjectMock
.Setup(service => service.ValidateUser(username, password.ToMd5Hash()))
.Returns(() => true);
或使用It.IsAny<>()
方法来放松模拟的期望...
usersBusinessObjectMock
.Setup(service => service.ValidateUser(It.IsAny<string>(), It.IsAny<string>()))
.Returns(() => true);
因此,传递给模拟的值无关紧要,它将始终返回true