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

时间:2009-09-15 02:35:50

标签: c# unit-testing moq

我无法对单元测试方法进行测试,该方法会更改传递给它的引用类型的某些属性。

举个例子,假设我有一个名为Policy的课程。

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

然后我将此政策传递给政策经理,以便取消政策。

policyManager.InactivatePolicy(policy);

inactivate策略方法执行以下操作:

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

我遇到的问题是单元测试这种DoSomething方法。 (忽略它在这个例子中所做的事情毫无用处的事实)

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

    policyManager.InactivatePolicy(policy);
}

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

[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      
}

所以我处在这样的情况下,代码在现实中工作,但在我的单元测试中被隔离时却没有。

我能够解决此问题的唯一方法是让策略管理器的InactivatePolicy方法返回修改过的 政策,以便我可以模拟预期的回报值。

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

}

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

有谁知道这有更好的方法吗?是否一个人基本上被迫返回原本打算通过传递给方法的参考值更新的值?

我的问题可能是策略管理器不应该有一个InactivatePolicy方法,而应该是在Policy对象本身以及稍后调用的数据库更新?

3 个答案:

答案 0 :(得分:6)

你不应该嘲笑PolicyManager,你应该嘲笑UpdatePolicy方法,因为你仍然想测试DoSomething方法的功能。

另外,你可能在树上测得太高了。

您应该单独测试InactivatePolicy()方法并仅测试该功能是否正常,然后再次在隔离中测试DoSomething()方法。

这里有2个单独的代码单元,你应该有单元测试来专门测试每个单元。

答案 1 :(得分:0)

<强>漫步

我不太关注。

如果您没有测试它通过NHibernate设置为true的事实(即您将假设它有效),那么您甚至还在测试什么?为什么还要考虑测试值,因为在生产代码中您只是假设它是如此?即使你琐碎地模拟了一个将其设置为true的系统,我也没有看到这一点,因为它与生产代码不同。

充其量,我会考虑拥有一个'模拟'数据存储;不是nHibernate,它什么都不做。在这种情况下,它将在UpdatePolicy类中实现,其中包含一些'MockRepo'而不是'nHibernateRepo'。

通过这种方式,如果你适当地设置了回购,你可以看到它被设置了。

虽然我很想知道它的意思,因为也许你的nHibernate代码有一个bug,实际上你所检查的只是一个布尔值的设置。

<强>摘要

为什么不只有一个测试dev db,你可以运行这个测试?

答案 2 :(得分:0)

我认为测试是错误的,因为实际上您正在测试模拟对象及其对策略对象的Active状态的影响,而不是测试您的原始对象,即使测试在真实场景中传递,PolicyManager也可能表现不同并导致DoSomething失败。 也许你最好在单元测试中测试PolicyManager及其UpdateInactive方法并且有一个 完整性测试,以测试DoSomething和真正的PolicyManager。