18

我正在尝试学习如何使用 C# 和 Moq 进行单元测试,并且我已经建立了一个小测试环境。鉴于此代码:

public interface IUser
{

    int CalculateAge();
    DateTime DateOfBirth { get; set; }
    string Name { get; set; }
}

public class User : IUser
{
    public DateTime DateOfBirth { get; set; }
    string Name { get; set; }

    public int CalculateAge()
    {
        return DateTime.Now.Year - DateOfBirth.Year;
    }
}

我想测试方法CalculateAge()。为此,我认为我应该尝试DateOfBirth通过在我的测试方法中执行此操作来为属性提供默认值:

var userMock = new Mock<IUser>();
userMock.SetupProperty(u => u.DateOfBirth, new DateTime(1990, 3, 25)); //Is this supposed to give a default value for the property DateOfBirth ?
Assert.AreEqual(22, userMock.Object.CalculateAge());

但是当它涉及到断言时,值CalculateAge()等于0,虽然DateOfBirth等于new DateTime(1990, 3, 25)

我知道这可能看起来像一个愚蠢的例子,但无论如何......我想我可以使用模拟来为我的对象中尚未开发的方法/属性赋予值,因此方法的测试不会依赖于另一个组件我的班级,甚至为我的对象设置默认上下文(因此这里的用户名......)我是否以错误的方式处理这个问题?

谢谢。

4

2 回答 2

23

是的,你接近它错了,但别担心,我会解释为什么。第一个提示是

你可以完全删除你的User班级,一切都会一样。

当你在做:

var userMock = new Mock<IUser>();

您只是创建了该接口的假\模拟对象,与您的初始类无关User,因此它没有任何CalculateAge方法的实现,除了只是愚蠢地返回 0 的假的方法。这就是为什么你得到 0你的断言声明。

所以,你说:

以为我可以使用模拟来为对象中尚未开发的方法/属性赋予值,因此方法的测试不会依赖于我的类的另一个组件

您可以,假设您将拥有一些 IUser 消费者,可以这样说:

class ConsumerOfIUser
{
   public int Consume(IUser user)
   {
      return user.CalculateAge() + 10;
   }
}

在这种情况下,模拟 IUser 将完全有意义,因为您想测试ConsumerOfIUserIUser.CalculateAge() 返回 10 时的行为。您将执行以下操作:

var userMock = new Mock<IUser>();
userMock.Setup(u => u.CalculateAge()).Returns(10);

var consumer = new ConsumerOfIUser();
var result = consumer.Consume(userMock);

Assert.AreEqual(result, 20); //should be true
于 2012-05-25T19:21:38.147 回答
2

这取决于您要测试的内容。在这种情况下,您已经模拟了 User 对象,因此在将其替换为模拟对象时,测试此类内部的任何内容都没有意义。如果你想测试 User 对象,那么你不应该模拟它。

模拟用于替换您不想测试的依赖对象。例如,如果您有一个 Name 对象而不是字符串(例如,包含名字、姓氏、头衔等),但您不想测试 Name 对象,只测试 User 对象,您将创建一个模拟构造用户对象时要使用的名称对象。

于 2012-05-25T19:22:22.077 回答