EricSchaefer
EricSchaefer

Reputation: 26330

Why is RhinoMocks throwing an InvalidOperationException at my test?

This is a test I am currently working on:
(Edited according to Lees answer)

[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();
}

Running the test results in:

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()

Any ideas?

Edit:
I just figured out that it works fine if the first line of the method is changed:

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

(string.Empty instead of null) But the test is supposed to check what happens if the Name property is null, so having Name being anything else but null would not be very helpful.

Edit:
The code is actually not testing what i I wanted to test. The first line of the method should be

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

Thats my expected object. LockClass is a DTO with only the two properties initialized in above line and the obligatory Equals stuff.
The act line should be

controller.AddLockClass( lockClass.Id, null );

[SetUp] creates all the mocked objects. What I am trying to test is that if the user creates a LockClass object though GUI gestures (the GUI calls AddLockClass on the controller) where the name is null the controller creates an object with an empty name. There are other ways to do that, but right now it has to be this way. The changed code does work (e.g. Rhino does not throw up). I am still keeping the question because it is curious why Rhino does not like the original code.

To make it complete:

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>();
}

Edit:

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();
    }
}

Upvotes: 4

Views: 2539

Answers (2)

Grzenio
Grzenio

Reputation: 36649

Rhino Mocks gives you correct exception. In your AddLockClass you have the following line:

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

which clearly indicates that if you call this method with null name parameter, it will still use empty string when creating the LockClass instance. So as you noticed in the first edit of your question, the LockClass that you should expect in your test is:

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

and not the version with null. Let me know if you need further clarification.

Upvotes: 3

Lee
Lee

Reputation: 144136

I think you need to enclose the 'act' part of the test inside a playback scope:

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

mockRepository.VerifyAll();

Upvotes: 2

Related Questions