代码之家  ›  专栏  ›  技术社区  ›  mezoid

如何对期望通过引用更新对象的方法进行单元测试?

  •  2
  • mezoid  · 技术社区  · 15 年前

    我在单元测试一个方法时遇到了问题,这个方法改变了传递给它的引用类型的一些属性。

    例如,假设我有一个名为policy的类。

    Policy policy = new Policy();
    policy.Status = Active;
    

    然后,我将此策略传递给策略管理器以停用策略。

    policyManager.InactivatePolicy(policy);
    

    停用策略方法执行以下操作:

    public void InactivatePolicy(Policy policy)
    {
        policy.Status = Inactive;
        UpdatePolicy(policy); //saves the updated policy details via nhibernate
    }
    

    我遇到的问题是这个剂量测量方法的单元测试。(忽略这样一个事实:在这个例子中它所做的是无用的)

    public void DoSomething(Policy policy)
    {
        Policy policy = new Policy();
        policy.Status = Active;
    
        policyManager.InactivatePolicy(policy);
    }
    

    因为我模拟了策略管理器,所以状态不会设置为不活动 因此,当我断言给药后被称为 策略处于非活动状态。测试失败,因为它仍然处于活动状态。

    [Test]
    public void TheStatusShouldBeInactiveWhenWeDoSomething()
    {
        Mock<IPolicyManager> policyManagerMock = new Mock<PolicyManager>();
        MyClass mc = new MyClass(policyManagerMock.Object);
    
        Policy policy = new Policy();
        policy.Status = Active;
    
        mc.DoSomething(policy);
    
        Assert.That(policy.Status, Is.EqualTo(Inactive)); //this fails      
    }
    

    所以我处于这样一种情况下,代码在现实中工作,但在我的单元测试中并不孤立。

    我能解决这个问题的唯一方法是让策略管理器的inactivepolicy方法返回修改后的 策略,以便我可以模拟预期的返回值。

    public Policy InactivatePolicy(Policy policy)
    {
        policy.Status = Inactive;
        UpdatePolicy(policy); //saves the updated policy details via nhibernate
        return policy;
    }
    
    [Test]
    public void TheStatusShouldBeInactiveWhenWeDoSomething()
    {
        Mock<IPolicyManager> policyManagerMock = new Mock<PolicyManager>();
        MyClass mc = new MyClass(policyManagerMock.Object);
    
        Policy expectedInactivePolicy = new Policy();
        expectedInactivePolicy.Status = Inactive;
    
        Policy policy = new Policy();
        policy.Status = Active;
    
        policyManagerMock
            .Setup(p => p.InactivatePolicy(policy))
            .Returns(expectedInactivePolicy);   
    
        mc.DoSomething(policy);
    
        Assert.That(policy.Status, Is.EqualTo(Inactive)); //this now succeeds
    
    }
    

    通常,当我在努力进行单元测试时,这表明我做错了事情。

    有人知道这样做是否有更好的方法吗?是否强制返回最初通过传递给方法的引用值更新的值?

    我的问题可能是策略管理器不应该有一个非活动的epolicy方法,而是应该在策略对象本身和稍后调用的数据库更新上?

    3 回复  |  直到 15 年前
        1
  •  6
  •   lomaxx    15 年前

    您不应该模拟policyManager,您应该模拟updatepolicy方法,因为您仍然希望测试dosometing方法的功能。

    而且,你可能在树上测试得太高了。

    您应该在隔离状态下测试inactivepolicy()方法,并且只测试该功能是否有效,然后应该在隔离状态下再次测试dosomething()方法。

    这里有两个独立的代码单元,您应该有专门测试每个单元的单元测试。

        2
  •  0
  •   Noon Silk    15 年前

    漫步

    我不太明白。

    如果你不测试它被设置为 true 通过NHibernate(也就是说,你会认为这是可行的),那么你还要测试什么呢?考虑到在生产代码中,您只是假设值是这样的,那么为什么还要测试这个值的设置呢?即使你只是简单地模拟一个将其设置为真的系统,我也不明白这一点,因为它与生产代码不同。

    充其量,我会考虑有一个“嘲弄”的数据存储,而不是NHibernate,那什么都不做。在这种情况下,它将在 UpdatePolicy 类,用一些“mockrepo”而不是“nhibernaterepo”。

    这样,如果您适当地设置回购,您可以看到它正在被设置。

    尽管我想知道它的意义,因为也许你的nhibernate代码有一个bug,实际上你要检查的只是一个布尔值的设置。

    总结

    为什么不让一个测试开发数据库来运行这个测试呢?

        3
  •  0
  •   Beatles1692    15 年前

    我认为测试是错误的,因为实际上您正在测试模拟对象及其对策略对象的活动状态的影响,而不是测试原始对象,即使在真实场景中测试通过,policyManager的行为可能会有所不同,并导致DoSometing失败。 也许您最好在单元测试中测试policyManager及其updateinactive方法,并 完整性测试,与真正的PolicyManager一起测试剂量。