【问题标题】:Why does the following test fail using Mbunit and Rhino mocks?为什么以下测试使用 Mbunit 和 Rhino 模拟失败?
【发布时间】:2010-09-09 15:34:24
【问题描述】:

我正在玩 MbUnit 和 Rhino Mocks 并做了一个简单的测试。它可能是设计不佳的代码,但我更专注于看看我是否能让测试通过。基本上,当汽车的发动机灯亮起时,汽车应该换油。这是代码:

public interface ICar
{
    bool EngineLight { get; set; }
    void GetOilChange();
    bool CheckEngineLight(ICar car);
}

public class Car : ICar
{
    public bool EngineLight { get; set; }

    public void GetOilChange()
    {
    }

    public bool CheckEngineLight(ICar car)
    {
        if (car.EngineLight)
            GetOilChange();
            return true;

        return false;
    } 
}

[TestFixture]
public class CarTests
{
[Test]
    public void WhenEngineLightIsOnGetOilChange()
    {
        var carMock = MockRepository.GenerateMock<ICar>();
        carMock.Stub(x => x.EngineLight).Return(true);

        Assert.AreEqual(true, new Car().CheckEngineLight(carMock)); //This passes

        carMock.AssertWasCalled(x => x.GetOilChange()); //This fails
    }
 }

【问题讨论】:

  • 我认为这与您的测试无关,但您的 CheckEngineLight() 代码中存在错误。 if 语句中的两行应该有 {}。
  • 我想我是故意遗漏的,因为如果 EngineLight 开启,我希望它调用 GetOilChange,它什么都不做,然后返回 true,但你是对的。在这种情况下,我知道它会返回 true,所以我故意将其省略。
  • 您在询问错误的对象是否调用了 GetOilChange 方法。 GetOilChange 方法是在您正在创建的 Car 对象上调用的,而不是在 mock 上。

标签: c# unit-testing rhino-mocks mbunit


【解决方案1】:

在这个方法中:

public bool CheckEngineLight(ICar car)
{
    if (car.EngineLight)
        GetOilChange();
        return true;

    return false;
} 

您正在调用 GetOilChange();在您正在测试的对象上,而不是在您作为参数传递的模拟上。因此,确实没有在模拟上调用此方法。我猜你想要这个:

public bool CheckEngineLight(ICar car)
{
    if (car.EngineLight)
    {
        car.GetOilChange();
        return true;
    }
    return false;
} 

【讨论】:

  • 谢谢。您能否详细说明我正在测试的对象而不是模拟对象的含义。
  • @Xaisoft,您有一个汽车对象(实现了 ICar 接口),该对象将另一个 ICar 接口作为参数。从设计的角度来看,不清楚为什么要以这种方式实现 - 也不清楚您想要换油的对象是什么。在您的原始实现中,您在具体实现(您正在测试)而不是参数(您已模拟)上更换了机油 - 因此模拟的汽车没有更换机油。
【解决方案2】:

首先,这段代码有一个错误。在 IF 周围添加 {}:

public bool CheckEngineLight(ICar car)
    {
        if (car.EngineLight)
        {
            car.GetOilChange();
            return true;
        }

        return false;
    } 

它失败的原因是因为您正在调用 new Car().CheckEngineLight ... 而 new Car() 正在调用 GetOilChange ... carMock 没有调用 GetOilChange。将代码更改为car.GetOilChange()(参见上面的代码)。

您将 Car 对象传递给 Car 对象的方法这一事实非常令人困惑。为什么不把代码改成这样:

public class Car : ICar
{
    public bool EngineLight { get; set; }

    public void GetOilChange()
    {
    }

    public bool CheckEngineLight()
    {
        if (EngineLight)
        {
            GetOilChange();
            return true;
        }

        return false;
    } 
}

将您的测试更改为:

[Test]
    public void WhenEngineLightIsOnGetOilChange()
    {
        var carMock = MockRepository.GenerateMock<ICar>();
        carMock.Stub(x => x.EngineLight).Return(true);

        Assert.AreEqual(true, carMock.CheckEngineLight()); 

        carMock.AssertWasCalled(x => x.GetOilChange()); 
    }
 }

【讨论】:

  • 但 CheckEngineLight 不调用 GetOilChange()
  • 我的意思是,我想确保在 CheckEngineLight 方法中调用了 GetOilChange。
  • 它不会为传递给 CheckEngineLight 的对象调用它...它为调用 CheckEngineLight 的对象调用它。
  • 另外,当我创建一个控制台应用程序并单步执行它时,它调用了 GetOilChange 方法,所以我看不出它是如何失败的。
  • 您能否详细说明您的评论。一旦我添加了 car.GetOilChange(),它就通过了,但是我对你的意思有点困惑它没有为传递给 CheckEngineLight 的对象调用它,它为调用 CheckEngineLight 的对象调用它跨度>
【解决方案3】:

您的测试有问题,仅此而已。改变

GetOilChange(); // calls the car instance, not the mock, your
                // AssertMethodCalled will never be true

car.GetOilChange(); // calls the mock you passed in

顺便说一句,这对 Rhino 来说可能更惯用:

public interface ICar
{
    bool EngineLight { get; set; }
    void GetOilChange();
    bool CheckEngineLight();
}

public class Car : ICar
{
    public bool EngineLight { get; set; }

    public virtual void GetOilChange()
    {
    }

    public virtual bool CheckEngineLight()
    {
        if (EngineLight)
        {
            GetOilChange();
            return true;
        }

        return false;
    }
}

[TestFixture]
public class CarTests
{
    [Test]
    public void WhenEngineLightIsOnGetOilChange()
    {
        MockRepository mocks = new MockRepository();

        Car car;
        using (mocks.Record())
        {
            car = mocks.PartialMock<Car>();
            car.EngineLight = true;

            car.Expect(x => x.GetOilChange())
                .Repeat.Once()
                .Message("Should have called GetOilChange");

        }

        using (mocks.Playback())
        {
            var res = car.CheckEngineLight();
            Assert.IsTrue(res);
        }
    }
}

【讨论】:

  • PartialMock 将调用对象的原始成员,除非您以期望明确地模拟它们。
  • 谢谢,我在完成 car.GetOilChange 后让它开始工作。现在说得通了。我是嘲笑的新手,我的 OO 设计有点蹩脚。我确实从 CheckEngineLight 中删除了 car 参数,只是调用了 GetOilChange(),但现在它失败了,因为它说它期望值为 true,但实际值为 false。
  • PartialMock 会调用对象的原始成员是什么意思?
  • @Xaisoft:Rhino 支持几种不同类型的模拟(StrictMockPartialMockDynamicMockStub),它们的行为各不相同。该文档没有充分描述这些差异以及何时使用一种或另一种类型的模拟 - 对于您的特定需求,您希望使用PartialMock,因为这种类型不会在没有期望的情况下覆盖方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多