【问题标题】:When should I assert the actions of the SUT?我应该何时断言 SUT 的操作?
【发布时间】:2014-09-03 09:04:57
【问题描述】:

考虑以下单元测试:

[Test]
public void Success()
{
  var userID = 5;
  var user = new User();
  var dataAccess = new Mock<IDataAccess>();
  dataAccess.Setup(x => x.GetUser(userID)).Returns(user).Verifiable();
  var sut = new UserController(dataAccess.Object);

  var returnedUser = sut.GetUser(userID);

  Assert.AreSame(user, returnedUser);
  dataAccess.Verify(x => x.GetUser(userID), Times.Once());
}

最近的阅读表明我应该只使用模拟来提供用户,并且应该有一个断言(AreSame 那个)。显然,理想的单元测试不应该担心验证 sut 的行为。

更多这样的:

[Test]
public void Success()
{
  var userID = 5;
  var user = new User();
  var dataAccess = new Mock<IDataAccess>();
  dataAccess.Setup(x => x.GetUser(userID)).Returns(user);
  var sut = new UserController(dataAccess.Object);

  var returnedUser = sut.GetUser(userID);

  Assert.AreSame(user, returnedUser);
}

这种简化的测试更好吗?单元测试应该断言它们调用的方法的行为还是只断言结果?

【问题讨论】:

标签: unit-testing moq


【解决方案1】:

一个基本的想法是在测试中只有一个断言。所以也许你应该在这里考虑两个测试。第一个基于价值的测试很重要。在这种情况下,第二次交互测试可能不适合。根据 Roy Osherove 的说法:“调用另一个对象时使用交互测试是特定工作单元的最终结果。”。

例子:

namespace Tests
{
    [TestClass]
    public class UserControllerTest
    {
        [TestMethod]
        public void GetUser_WhenCalled_ReturnsUserSameAsDataAccess()
        {
            // Arrange
            const int userID = 5;
            User expectedUser = new User();

            Mock<IDataAccess> dataAccessStub = new Mock<IDataAccess>();
            dataAccessStub.Setup(x => x.GetUser(userID)).Returns(expectedUser);
            UserController controller = new UserController(dataAccessStub.Object);

            // Act
            User actualUser = controller.GetUser(userID);

            // Assert
            Assert.AreSame(expectedUser, actualUser);
        }

        [TestMethod]
        public void GetUser_WhenCalled_GetUserOnDataAccessIsCalledOnce()
        {
            // Arrange
            const int userID = 5;
            const int getUserCallsCountExpected = 1;
            int getUserCallsCount = 0;

            Mock<IDataAccess> dataAccessMock = new Mock<IDataAccess>();
            dataAccessMock.Setup(x => x.GetUser(userID)).Callback(() => getUserCallsCount++);
            UserController controller = new UserController(dataAccessMock.Object);

            // Act
            controller.GetUser(userID);

            // Assert
            Assert.AreEqual(getUserCallsCountExpected, getUserCallsCount);
        }
    }
}

【讨论】:

  • 我同意第一次测试。但我总是难以理解第二次测试增加了什么价值?不是在测试API的内部实现吗?这个测试对重构有弹性吗?只要公共行为不改变,单元测试不应该继续通过吗?
  • @Amol 是的,我认为你是对的,第二个测试可能不适合这种情况。我编辑了答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-03-25
  • 1970-01-01
  • 1970-01-01
  • 2018-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多