如何为起订量中的属性赋值?

Posted

技术标签:

【中文标题】如何为起订量中的属性赋值?【英文标题】:How to assign values to properties in moq? 【发布时间】:2013-05-24 20:53:38 【问题描述】:

我有一个类,其方法返回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 as 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 方法的 Fake 存储库来保存/更新对象:

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'创建用户时,用户会返回Mockfilled;

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

问题是邮箱、姓名、密码都是空的(有默认值)!

【问题讨论】:

你在这里测试哪个类?我只看到周围的嘲笑 我正在测试我的CustomMembershipProviderstatus.Should().Be(MembershipCreateStatus.Success); 仓库中创建的用户,邮箱、姓名、密码为空。 CustomMembershipProvider 是您代码中的模拟。您正在测试起订量框架? @SergeyBerezovskiy CustomMembershipProvider 是一个模拟,因为他模拟了这个类上的一个方法,但仍然为每个其他方法调用基本实现(因为.CallBase = true 【参考方案1】:

你准备模拟用户的方式是个问题。

moqUser.Object.Name = username;

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

moqUser.SetupAllProperties();

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

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

另一种方法是:

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

return mockUser;

【讨论】:

你也可以使用SetupProperty一次做一个,也可以 谢谢,我会扩展答案 我不认为你可以.Verify这样设置属性值【参考方案2】:

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

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

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

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

【讨论】:

理论上你是对的。除非被测方法在同一个类上调用另一个虚拟方法,否则 CallBase = true 会起作用。我同意这是一个糟糕的设计,但有时人们会使用遗留代码等。 @SunnyMilenov 抱歉,但实际上我从不测试模拟。是否有任何测试模拟有用的示例? @lazyberezovsky 很好的解释。我同意你的看法。在这种特殊情况下,我测试CustomMembershipProvider,特别是用户的创建:CreateUser 方法.. .. 在CreateUser 方法中,创建对象User 调用GetUser 方法。但是我的User 对象的Id 属性设置器受保护(Id 由数据库生成)... @lzayberezovsky:在这个具体示例中,他不是在测试模拟,而是在测试 CustomMembershipProviderClass。他只是使用 moq 作为助手,让他覆盖它的一个特定方法,而不是创建一个继承的类,并在那里覆盖。在这种情况下,这不是模拟,而是存根。【参考方案3】:

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

mock.SetupAllProperties();

【讨论】:

以上是关于如何为起订量中的属性赋值?的主要内容,如果未能解决你的问题,请参考以下文章

起订量中 VerifyAll() 的用途是啥?

使用最小起订量模拟静态属性

如何最小起订量实体框架 SqlQuery 调用

起订量 IServiceProvider / IServiceScope

带参数的起订量 ReturnsAsync()

未找到统一起订量