70

我有一个带有返回类型对象的方法的类User

public class CustomMembershipProvider : MembershipProvider
{
    public virtual User GetUser(string username, string password, string email, bool isApproved)
    {
        return new User()
            {
                Name = username
                ,Password = EncodePassword(password)
                ,Email = email
                ,Status = (isApproved ? UsuarioStatusEnum.Ativo : UsuarioStatusEnum.ConfirmacaoPendente)
                // ...
            };
    }

    // ..
}

User是一个域对象。请注意将setter 设置为 protectedId的属性:

public class User : IAuditable, IUser
{
    public virtual int Id { get; protected set; }
    public virtual string Name { get; set; }
    public virtual string Email { get; set; }
    public virtual UsuarioStatusEnum Status { get; set; }
    public virtual string Password { get; set; }
}

Id 受到保护,因为它是由数据库生成的。

测试项目

在我的测试项目中,我有一个带有Store保存/更新对象的方法的假存储库:

public void Store(T obj)
{
    if (obj.Id > 0)
        _context[obj.Id] = obj;
    else
    {
        var generateId =  _context.Values.Any() ? _context.Values.Max(p => p.Id) + 1 : 1;
        var stubUser = Mock.Get<T>(obj); // In test, will always mock
        stubUser.Setup(s => s.Id).Returns(generateId);
        _context.Add(generateId, stubUser.Object);
    }
}

CustomMembershipProvider我有public override MembershipUser CreateUser调用GetUser创建一个User.
这样,我所要做的就是模拟该GetUser方法,以便存储库可以生成Id

var membershipMoq = new Mock<CustomMembershipProvider>();
membershipMoq.CallBase = true;
membershipMoq
    .Setup(p => p.GetUser(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>()))
    .Returns<string, string, string, bool>( (username, password, email, isAproved) => {
        var moqUser = new Mock<User>();
        moqUser.Object.Name = username;
        moqUser.Object.Password = password;
        moqUser.Object.Email = email;
        moqUser.Object.Status = (isAproved ? UsuarioStatusEnum.Ativo : UsuarioStatusEnum.ConfirmacaoPendente);
        return moqUser.Object;
    });
_membershipProvider = membershipMoq.Object;

问题

理论上一切都是正确的。CreateUser调用GetUser创建用户时,用户返回Mock填充;

[TestMethod]
public void CreateUser_deve_criar_usuario_no_repositorio()
{
    // Act
    MembershipCreateStatus status;
    var  usr = _membershipProvider.CreateUser(
        _fixture.Create<string>(),
        _fixture.Create<string>(),
        _fixture.Create<string>(),
        null, null, true, null,
        out status);

    // usr should have name, email password filled. But not!

    // Assert
    status.Should().Be(MembershipCreateStatus.Success);
}

问题是电子邮件、姓名、密码为空(使用默认值)!

在此处输入图像描述

4

3 回答 3

133

您准备模拟用户的方式是问题所在。

moqUser.Object.Name = username;

除非您正确设置了模拟,否则不会设置名称。在给属性赋值之前试试这个:

moqUser.SetupAllProperties();

此方法将准备模拟上的所有属性,以便能够记录分配的值,并在以后重播(即充当真实属性)。

您还可以使用SetupProperty()方法来设置各个属性,以便能够记录传入的值。

另一种方法是:

var mockUser = Mock.Of<User>( m =>
    m.Name == "whatever" &&
    m.Email == "someone@example.com"); 

return mockUser;
于 2013-05-29T15:45:17.023 回答
11

我认为您缺少嘲笑的目的。用于模拟您正在测试的类的依赖项的模拟:

在此处输入图像描述

被测系统 (SUT) 应单独进行测试(即与其他单元分开)。否则,依赖项中的错误将导致您的 SUT 测试失败。此外,您不应该为模拟编写测试。这没有给你任何东西,因为模拟不是生产代码。模拟不会在您的应用程序中执行。

CustomMembershipProvider因此,只有在测试某个依赖于它的单元时才应该模拟(顺便说一句,最好创建一些像接口这样的抽象ICustomMembershipProvider来依赖)。

或者,如果您正在为CustomMembershipProvider类编写测试,则不应模拟它 - 仅应模拟此提供程序的依赖项。

于 2013-05-29T15:42:09.467 回答
0

指定模拟上的所有属性都应该具有“属性行为”,这意味着设置它们的值将导致它们被保存并稍后在请求属性时返回。(这也称为“存根”。)每个属性的默认值将是由模拟属性指定的生成的值。

mock.SetupAllProperties();
于 2021-04-21T17:48:42.343 回答