如何使用Rhino Mock来模拟本地函数调用?

时间:2009-09-08 03:01:13

标签: unit-testing rhino-mocks

这是我的情况: 我想测试“HasSomething()”函数,它在以下类中:

public class Something
{
      private object _thing;

      public virtual bool HasSomething()
      {
           if (HasSomething(_thing))
              return true;
           return false;
      }

      public virtual bool HasSomething(object thing)
      {
           ....some algo here to check on the object...
           return true;
      }
}

所以,我把我的测试写成这样:

    public void HasSomethingTest1()
    {
        MockRepository mocks = new MockRepository();

        Something target = mocks.DynamicMock(typeof(Something)) as Something;

        Expect.Call(target.HasSomething(new Object())).IgnoreArguments().Return(true);

        bool expected = true;
        bool actual;
        actual = target.HasSomething();

        Assert.AreEqual(expected, actual);
    }

我的测试是否写得正确? 请帮助我,因为我甚至无法得到预期的结果。 “HasSomething(object)”就是不能以这种方式嘲笑。它并没有像我期待的那样让我'真实'。

感谢。

3 个答案:

答案 0 :(得分:2)

回应OP的'回答':你的主要问题是RhinoMocks不会模拟类的成员 - 而是创建模拟类,然后我们可以为其成员设置期望和预制响应(即属性和功能)。如果您尝试测试模拟/存根类的成员函数,则存在测试模拟框架而非实现的风险。

对于逻辑路径依赖于本地(通常是私有)函数的返回值的特定场景,您确实需要一个外部依赖项(另一个对象),它将影响您从该本地函数所需的返回值。对于上面的代码片段,我会按如下方式编写测试:

[Test]
public void TestHasSomething()
{
    // here I am assuming that _thing is being injected in via the constructor
    // you could also do it via a property setter or a function
    var sut = new Something(new object()); 
    Assert.IsTrue(sut.HasSomething);
}

即。不需要嘲笑。


这是我过去常常在嘲笑方面的一个误解点;我们模拟被测系统(SUT)的依赖行为。类似的东西:SUT调用依赖的几个方法,而模拟过程提供预设的响应(而不是去数据库等)来指导逻辑流的方式。

一个简单的例子是如下(请注意,我已经使用RhinoMocks AAA语法进行此测试。顺便说一句,我注意到您使用您的代码示例中的语法使用记录 - 重放模式,不同之处在于它没有使用Record和Replay!这可能会导致问题):

public class SUT
{

    Dependency _depend

    public SUT (Dependency depend)
    {
        _depend = depend;
    }

    ...

    public int MethodUnderTest()
    {
        if (_depend.IsReady)
              return 1;
        else
              return -1;
    }
}

...

[Test]
public void TestSUT_MethodUnderTest()
{
    var dependency = MockRepository.GenerateMock<Dependency>();

    dependency.Stub(d => d.IsReady).Return(true);

    var sut = new SUT(dependency);
    Assert.AreEqual(1, sut.MethodUnderTest());
}

所以你遇到的问题是你试图测试一个模拟对象的行为。这意味着你根本没有真正测试你的课程!

答案 1 :(得分:0)

在这种情况下,你的test double应该是Something类的派生版本。然后重写方法HasSomething(object)并确保HasSomething()调用您的方法。

答案 2 :(得分:0)

如果我理解正确,你实际上对测试方法HasDynamicFlow感兴趣(在上面的例子中没有描述),而不考虑你自己的HasSomething算法。

Preet是正确的,你可以简单地继承Something并覆盖HasSomething的行为以使算法短路,但这需要创建一些额外的测试虚拟代码,Rhino可以有效地消除这些代码。

考虑使用 Partial Mock Stub而不是动态模拟。存根不太严格,非常适合使用Properties。然而,方法需要一些额外的努力。

    [Test]
    public void CanStubMethod()
    {
        Foo foo = MockRepository.GenerateStub<Foo>();

        foo.Expect(f => f.HasDynamicFlow()).CallOriginalMethod(OriginalCallOptions.NoExpectation);
        foo.Expect(f => f.HasSomething()).CallOriginalMethod(OriginalCallOptions.NoExpectation);
        foo.Expect(f => f.HasSomething(null)).IgnoreArguments().Return(true);

        Assert.IsTrue(foo.HasDynamicFlow());
    }

编辑:添加了代码示例并将Partial Mock切换为Stub