【问题标题】:Test a controller in .Net Core always returns false?在 .Net Core 中测试控制器总是返回 false?
【发布时间】:2018-04-28 19:37:10
【问题描述】:

我编写了以下 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

怎么了?

【问题讨论】:

  • 如果你只是将值转换为布尔值会发生什么,即var result = (bool) okObjectResult.Value;
  • @Nkosi:结果总是错误的,请参阅主帖图片中的观看部分
  • 我们专注于错误的事情。模拟未正确配置,因此未按预期运行。检查我提供的答案。

标签: c# unit-testing asp.net-core moq xunit.net


【解决方案1】:

在阅读您的代码 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;

【讨论】:

  • 请注意Assert.IsType(T) 假定将提供的对象转换为提供的类型。
  • 我像你写的那样改变我的代码,没有任何改变,而且 okObjectResult 的值总是假的!
【解决方案2】:

这会起作用,我会在你的代码中进行一些小的重构。

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);
    }
  }
}

【讨论】:

  • 我像你写的那样改变我的代码,没有任何改变,actual.Value 总是假的!
【解决方案3】:

在重新检查控制器如何调用验证方法后,我意识到问题在于您没有正确配置模拟。

控制器正在调用

_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&lt;&gt;() 方法来放松对模拟的期望...

usersBusinessObjectMock
    .Setup(service => service.ValidateUser(It.IsAny<string>(), It.IsAny<string>()))
    .Returns(() => true);

所以无论你传递什么值给模拟,它总是会返回true

【讨论】:

  • 太好了,非常感谢
猜你喜欢
  • 2020-10-04
  • 1970-01-01
  • 1970-01-01
  • 2020-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-06
  • 1970-01-01
相关资源
最近更新 更多