在 .Net Core 中测试控制器总是 returns false?
Test a controller in .Net Core always returns false?
我编写了以下 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);
}
}
}
账户控制器:
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
必须 return true
:
usersBusinessObjectMock
.Setup(service => service.ValidateUser(username, password))
.Returns(() => true);
但是 var result = okObjectResult.Value as bool?;
总是 false
怎么了?
看完你的代码大约 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;
这会起作用,我会在您的代码中进行一些小的重构。
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);
}
}
}
在重新检查控制器如何调用验证方法后,我意识到问题是您没有正确配置模拟。
控制器正在调用
_usersBusinessObject.ValidateUser(model.Username, model.Password.ToMd5Hash())
请注意 ToMd5Hash()
正在调用密码。
然而,您将模拟设置为...
usersBusinessObjectMock
.Setup(service => service.ValidateUser(username, password))
.Returns(() => true);
看到问题了吗?
模拟需要原始密码而不是它的哈希值,因此在传递哈希密码时不会 return true
。这导致操作的对象结果总是 returning false
,因为模拟总是 returns 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);
所以不管你传递给模拟的值是什么,它总是 return true
我编写了以下 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);
}
}
}
账户控制器:
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
必须 return true
:
usersBusinessObjectMock
.Setup(service => service.ValidateUser(username, password))
.Returns(() => true);
但是 var result = okObjectResult.Value as bool?;
总是 false
怎么了?
看完你的代码大约 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;
这会起作用,我会在您的代码中进行一些小的重构。
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);
}
}
}
在重新检查控制器如何调用验证方法后,我意识到问题是您没有正确配置模拟。
控制器正在调用
_usersBusinessObject.ValidateUser(model.Username, model.Password.ToMd5Hash())
请注意 ToMd5Hash()
正在调用密码。
然而,您将模拟设置为...
usersBusinessObjectMock
.Setup(service => service.ValidateUser(username, password))
.Returns(() => true);
看到问题了吗?
模拟需要原始密码而不是它的哈希值,因此在传递哈希密码时不会 return true
。这导致操作的对象结果总是 returning false
,因为模拟总是 returns 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);
所以不管你传递给模拟的值是什么,它总是 return true