在.Net Core中测试一个控制器总是返回false?

时间:2017-11-15 14:17:58

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

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

怎么了?

Test Error Screen Shot

3 个答案:

答案 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