为什么RhinoMocks在我的测试中抛出InvalidOperationException?

时间:2010-07-02 09:31:22

标签: c# .net unit-testing tdd rhino-mocks

这是我目前正在进行的测试:
(根据李斯的回答编辑)

[Test]
public void AddLockClassWithNullNameShouldCallInsertOnSessionWithEmptyString()
{
    LockClass lockClass = new LockClass { Id = ValidId, Name = null };

    using ( mockRepository.Record() ) {
        sessionFactory.CreateSession();
        LastCall.Return( session );

        session.InsertWithId( lockClass );
        LastCall.Return( lockClass );

        session.Commit();
        session.Dispose();
    }

    using ( mockRepository.Playback() ) {
        controller = new LockClassPanelController( sessionFactory );
        controller.AddLockClass( lockClass.Id, string.Empty );
    }

    mockRepository.VerifyAll();
}

运行测试结果:

Test 'Test.Unit.Controllers.LockClassPanelControllerTests.AddLockWithNullNameClassShouldCallInsertOnSessionWithEmptyString' failed:
 System.InvalidOperationException : The operation is invalid because of the current object state. (translated from german, dunno if thats the original english wording)
 at System.Reflection.RuntimeMethodInfo.GetGenericMethodDefinition()
 at Rhino.Mocks.MethodRecorders.UnorderedMethodRecorder.MethodsEquals(MethodInfo method, ProxyMethodExpectationTriplet triplet)
 at Rhino.Mocks.MethodRecorders.UnorderedMethodRecorder.GetAllExpectationsForProxyAndMethod(Object proxy, MethodInfo method)
 at Rhino.Mocks.MethodRecorders.UnorderedMethodRecorder.CalcExpectedAndActual.Calculate(Object proxy, MethodInfo method, Object[] args)
 at Rhino.Mocks.MethodRecorders.UnorderedMethodRecorder.CalcExpectedAndActual..ctor(UnorderedMethodRecorder parent, Object proxy, MethodInfo method, Object[] args)
 at Rhino.Mocks.MethodRecorders.UnorderedMethodRecorder.UnexpectedMethodCall(IInvocation invocation, Object proxy, MethodInfo method, Object[] args)
 at Rhino.Mocks.MethodRecorders.UnorderedMethodRecorder.DoGetRecordedExpectation(IInvocation invocation, Object proxy, MethodInfo method, Object[] args)
 at Rhino.Mocks.MethodRecorders.MethodRecorderBase.GetRecordedExpectation(IInvocation invocation, Object proxy, MethodInfo method, Object[] args)
 at Rhino.Mocks.Impl.ReplayMockState.DoMethodCall(IInvocation invocation, MethodInfo method, Object[] args)
 at Rhino.Mocks.Impl.ReplayMockState.MethodCall(IInvocation invocation, MethodInfo method, Object[] args)
 at Rhino.Mocks.MockRepository.MethodCall(IInvocation invocation, Object proxy, MethodInfo method, Object[] args)
 at Rhino.Mocks.Impl.RhinoInterceptor.Intercept(IInvocation invocation)
 at Castle.DynamicProxy.AbstractInvocation.Proceed()
 at ISessionProxy2762dfaac4274133bc97e10d4e5c35d0.InsertWithId[TEntity](TEntity entity)
 Controllers\LockClassPanelController.cs(20,0): at Artea.Service.Controllers.LockClassPanelController.AddLockClass(Int32 id, String name)
 Unit\Controllers\LockClassPanelControllerTests.cs(80,0): at Test.Unit.Controllers.LockClassPanelControllerTests.AddLockWithNullNameClassShouldCallInsertOnSessionWithEmptyString()

有什么想法吗?

修改
我只是发现,如果方法的第一行被更改,它可以正常工作:

LockClass lockClass = new LockClass { Id = ValidId, Name = string.Empty };

string.Empty代替null) 但是测试应该检查如果Name属性为null会发生什么,所以让Name除了null之外的任何其他内容都不会有用。

修改
代码实际上没有测试我想要测试的内容。方法的第一行应该是

LockClass lockClass = new LockClass { Id = ValidId, Name = string.Empty };

这是我的预期目标。 LockClass是一个DTO,只有在上面的行中初始化了两个属性和强制性的Equals内容。 行为方式应为

controller.AddLockClass( lockClass.Id, null );

[SetUp]创建所有模拟对象。我要测试的是,如果用户通过GUI手势创建一个LockClass对象(GUI调用控制器上的AddLockClass),其名称为空,则控制器创建一个空名称的对象。还有其他方法可以做到这一点,但现在必须这样做。改变后的代码确实有效(例如Rhino不会呕吐)。我仍然坚持这个问题,因为很奇怪为什么Rhino不喜欢原始代码。

要完成它:

private const int ValidId = 4711;
private const int InvalidId = 0;
private MockRepository mockRepository;
private ISessionFactory sessionFactory;
private ISession session;
private LockClassPanelController controller;

[SetUp]
public void Setup()
{
    mockRepository = new MockRepository();
    sessionFactory = mockRepository.StrictMock<ISessionFactory>();
    session = mockRepository.StrictMock<ISession>();
}

修改:

public void AddLockClass( int id, string name )
{
    if ( id != 0 ) {
        using ( var session = sessionFactory.CreateSession() ) {
            session.InsertWithId( new LockClass { Id = id, Name = name } );
            session.Commit();
        }
        LoadLockClasses();
        view.Initialize();
    }
}

2 个答案:

答案 0 :(得分:3)

Rhino Mocks为您提供正确的例外。在AddLockClass中,您有以下行:

session.InsertWithId( new LockClass { Id = id, Name = ( name ?? string.Empty ) } );

清楚地表明,如果使用null name参数调用此方法,则在创建LockClass实例时仍将使用空字符串。正如您在第一次编辑问题时注意到的那样,您在测试中应该期待的LockClass是:

LockClass lockClass = new LockClass { Id = ValidId, Name = string.Empty };

版本为null。如果您需要进一步澄清,请与我们联系。

答案 1 :(得分:2)

我认为您需要在回放范围内包含测试的“act”部分:

using(mockRepository.Playback())
{
    controller = new LockClassPanelController( sessionFactory ); 
    controller.AddLockClass( lockClass.Id, string.Empty );
}

mockRepository.VerifyAll();