【问题标题】:Can't figure out why this Rhino Mock is failing? Error: Expected True, But Was: False无法弄清楚为什么这个 Rhino Mock 失败了?错误:预期为真,但实际为:假
【发布时间】:2010-09-09 21:41:13
【问题描述】:

代码如下:

public interface IAccessPoint
{
    int BackHaulMaximum { get; set; }

    bool BackHaulMaximumReached();
    void EmailNetworkProvider();
}

public class AccessPoint : IAccessPoint
{

    public int BackHaulMaximum { get; set; }

    public bool BackHaulMaximumReached()
    {
        if (BackHaulMaximum > 80)
        {
            EmailNetworkProvider();
            return true;
        }
        return false;
        }

    public void EmailNetworkProvider()
    {

    }
}

//Test
[Test]
public void NetworkProviderShouldBeEmailedWhenBackHaulMaximumIsReached()
{
        var apMock = MockRepository.GenerateMock<IAccessPoint>();

        apMock.Stub(x => x.BackHaulMaximum).Return(81);

        Assert.AreEqual(true, apMock.BackHaulMaximumReached());

        apMock.AssertWasCalled(x => x.EmailNetworkProvider());
 }

【问题讨论】:

  • 您发布代码并说它“失败”?你没有给出错误信息。
  • 它说它应该是真的,但是是假的,我不知道为什么。我在我的模拟上调用 BackHalMaximumReached() 并且 Backhaulmaximum 设置为 81,所以它应该返回 true。
  • 我有段时间没用了,BackHaulMaximum 不是必须是虚拟的才能被存根吗?
  • 不,我有类似但不同的示例,并且可以通过,但这不起作用。
  • @user - 接口上的所有方法都是虚拟的。

标签: c# rhino-mocks rhino


【解决方案1】:

您不应该嘲笑您正在测试的课程。您应该只模拟被测类所依赖的类。像这样的:

public interface IAccessPoint
{
    int BackHaulMaximum { get; set; }

    bool BackHaulMaximumReached();
    void EmailNetworkProvider();
}

public class AccessPoint : IAccessPoint
{
    private IMailProvider Mailer { get; set; }

    public AccessPoint( IMailProvider provider )
    {
        this.Mailer = provider ?? new DefaultMailProvider();
    }

    public int BackHaulMaximum { get; set; }

    public bool BackHaulMaximumReached()
    {
        if (BackHaulMaximum > 80)
        {
            EmailNetworkProvider();
            return true;
        }
        return false;
        }

    public void EmailNetworkProvider()
    {
        this.Mailer.SendMail(...);
    }
}

[Test]
public void NetworkProviderShouldBeEmailedWhenBackHaulMaximumIsReached()  
{  
    var mailerMock = MockRepository.GenerateMock<IMailProvider>();  

    mailerMock .Expect( m => m.SendMail( ... specify argument matches ... ) ); 

    var accessPoint = new AccessPoint( mailerMock ); 

    accessPoint.BackHaulMaximum = 81;

    Assert.IsTrue( accessPoint.BackHaulMaximumReached() );

    mailerMock.VerifyAllExpectations();
}

【讨论】:

  • 我有点困惑,你能澄清一下吗?
  • 我不同意。如果正在测试一个类的单个方法的功能,并且该功能有副作用(例如发送电子邮件),那么一定要模拟它。
  • 如果我的 Email 方法在我的 AccessPoint 类中,这仍然不能告诉我为什么 BackHaulMaximumReached() 返回 false 而不是 true。
  • @KeithS - 如果他在模拟类而不是可能的接口,但在这种情况下它不会起作用。无论如何,这不是我选择使用模拟的方式。如果您无法从状态中看出该方法已正确执行并且需要验证它是否“做了”其他事情,那么我会说该操作是它自己的类的主要候选者,因为它很可能与直接无关被测类的功能。
  • @Xaisoft - 它返回 false 因为您没有提供对它的期望,它只是返回默认值。请注意,您正在模拟接口而不是类,并且它不知道您的类实现是它应该使用的,因为您没有告诉它。
【解决方案2】:

我同意 tvanfosson 的回答。 99%(也许 100%)的时间里,您不会想要模拟您的 SUT。

但是它失败的原因是因为你的电话:

Assert.AreEqual(true, apMock.BackHaulMaximumReached()); 

不会导致调用实际方法 BackHaulMaximumReached()!它会调用 mock 的方法。

编辑

如果不清楚,这意味着 BackHaulMaximumReached() 将(我认为)返回其返回类型的默认值,在布尔值的情况下为“false”。因此,您需要删除该方法以返回 true 以使 Assert.AreEqual 能够通过(正如我所说,这不是一个好的测试)。

然后您的下一个断言将失败,因为 EmailNetworkProvider() 将永远不会被调用(再次,因为您正在调用模拟的方法,而不是实际的 SUT 的方法)。

【讨论】:

  • “被测对象”。换句话说,就是你要测试的类(或方法等)。
【解决方案3】:

我将代码更改如下:

    //Test
    [Test]
    public void NetworkProviderShouldBeEmailedWhenBackHaulMaximumIsReached()
    {

        var mockRepo = new MockRepository();
        var apMock = mockRepo.PartialMock<AccessPoint>();

        using (mockRepo.Record())
        {
            Expect.Call(apMock.EmailNetworkProvider).Repeat.Once();
        }
        using (mockRepo.Playback())
        {
            apMock.BackHaulMaximum = 81;
            Assert.AreEqual(true, apMock.BackHaulMaximumReached());
        }
        mockRepo.VerifyAll();
    }

您所期望的是,给定类的输入,当您调用一个方法时,也会调用另一个会引发一些不需要的副作用的方法。您想模拟引起副作用的行为,但您想练习其余的实际逻辑。

解决方案是 PartialMock。它允许模拟行为仅应用于您指定的模拟类的成员。您可以在多种语法中使用它,但最安全、最可靠的方法是记录和回放期望,而不是在模拟本身上调用 Expects() 方法。

我们在这里使用它来期待我们想要模拟的方法的调用;期待调用将导致不调用实际方法。然后,执行类,使用实际类的逻辑(因此我们测试中的断言成功),但是当调用到我们期望的方法时,实际调用的是更新一些内部计数器的模拟方法.然后,VerifyAll() 断言所有预期都按照规定的设置发生。

【讨论】:

  • keith,Record, Play 语法不是在 Rhino Mocks 中的老方法。
  • 旧但仍然完全可用;我发现这种语法是让 PartialMock 以您希望的方式运行的最可靠方法。
猜你喜欢
  • 1970-01-01
  • 2016-09-27
  • 1970-01-01
  • 1970-01-01
  • 2023-02-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多