犀牛嘲笑,空虚和属性

时间:2009-01-21 23:59:14

标签: c# tdd mocking rhino-mocks

刚开始使用Rhino Mocks并且我有一个非常简单的问题,如何使用设置属性的void来模拟一个类?

class SomeClass : ISomeClass
{
    private bool _someArg;

    public bool SomeProp { get; set; }

    public SomeClass(bool someArg)
    {
        _someArg = someArg; 
    }

    public void SomeMethod()
    {
        //do some file,wcf, db operation here with _someArg
        SomeProp = true/false;
    }
}

显然,这是一个非常人为的例子,谢谢。

4 个答案:

答案 0 :(得分:6)

在您的示例中,您不需要RhinoMocks,因为您显然正在测试被测试类的功能。简单的单元测试将改为:

[Test]
public void SomeTest()
{
    var sc = new SomeClass();
        // Instantiate SomeClass as sc object
    sc.SomeMethod();
        // Call SomeMethod in the sc object.

    Assert.That(sc.SomeProp, Is.True );
        // Assert that the property is true... 
        // or change to Is.False if that's what you're after...
}

当你有一个依赖于其他类的类时,测试mocks会更有趣。在你的例子中,你提到:

  

//使用_someArg

执行一些文件,wcf,db操作

即。你希望其他一些类设置SomeClass的属性,这对mocktest更有意义。例如:

public class MyClass {

    ISomeClass _sc;

    public MyClass(ISomeClass sc) {
        _sc = sc;
    }

    public MyMethod() {
        sc.SomeProp = true;
    }

}

所需的测试将是这样的:

[Test]
public void MyMethod_ShouldSetSomeClassPropToTrue()
{
    MockRepository mocks = new MockRepository();
    ISomeClass someClass = mocks.StrictMock<ISomeClass>();

    MyClass classUnderTest = new MyClass(someClass);

    someClass.SomeProp = true;
    LastCall.IgnoreArguments();
        // Expect the property be set with true.

    mocks.ReplayAll();

    classUndertest.MyMethod();
        // Run the method under test.

    mocks.VerifyAll();
}

答案 1 :(得分:3)

取决于您在模拟对象中保持多少保真度。做到这一点的简单方法是不要担心它,并写出一些愚蠢的期望陈述。

[Test]
public void SomeTest()
{
   MockRepository mocks = new MockRepository();
   ISomeClass mockSomeClass = mocks.StrictMock<ISomeClass>();
   using(mocks.Record())
   {
      using(mocks.Ordered())
      {
         Expect.Call(MockSomeClass.SomeProp).Return(false);
         Expect.Call(delegate{MockSomeClass.SomeMethod();});
         Expect.Call(MockSomeClass.SomeProp).Return(true);
      }
   }
}

如果你想要的东西更像真实物体而没有一组有序的反应,你必须在期望中使用do方法设置委托。

delegate bool propDelegate();
delegate void methodDelegate();
private bool m_MockPropValue = false;

[Test]
public void SomeTest()
{
   MockRepository mocks = new MockRepository();
   ISomeClass mockSomeClass = mocks.StrictMock<ISomeClass>();
   using(mocks.Record())
   {
      SetupResult.For(MockSomeClass.SomeProp).Do(new propDelegate(delegate
      {
         return this.m_MockPropValue;
      }));
      Expect.Call(delegate{MockSomeClass.SomeMethod();}).Do(new methodDelegate(delegate
      {
         this.m_MockPropValue = true;
      }));
   }
}

答案 2 :(得分:0)

当您使用SetupResult.ForExpect.Call准备某些内容时,您需要确保它们虚拟,否则RhinoMocks将无法自行实施。

否则,只需设置结果并按照 Scott Pedersen 显示

进行预期调用

答案 3 :(得分:0)

从问题中你要测试的对象是什么并不完全清楚 - 如果你只想检查SomeClass.SomeMethod()设置一个属性然后你不需要模拟,因为你可以做一个简单的基于状态的测试:

[TestMethod]
public void SomeMethodTest()
{
    SomeClass s = new SomeClass();
    s.SomeMethod();

    Assert.AreEqual(expectedValue, s.SomeProp);
}

或者,如果SomeClass是某个其他类的依赖项,并且您想测试该类与SomeClass之间的交互,那么您在使用RhinoMock的测试的“Record”部分期间设置方法调用的期望,像这样:

[TestMethod]
    public void CheckInteractionWithSomeClass()
    {
        MockRepository mocks = new MockRepository();
        ISomeClass someClass = mocks.StrictMock<ISomeClass>();

        using (mocks.Record())
        {
            //record expection that someClass.SomeMethod will be called...
            someClass.SomeMethod();
        }

        using (mocks.Playback())
        {
            //setup class under test - ISomeClass is injected through the constructor here...
            ClassUnderTest o = new ClassUnderTest(someClass);

            o.MethodOnClassUnderTestThatShouldCallSomeClass.SomeMethod();

            //any other assertions...
        }
    }