【问题标题】:Why is RhinoMocks throwing an InvalidOperationException at my test?为什么 RhinoMocks 在我的测试中抛出 InvalidOperationException?
【发布时间】:2010-07-02 09:31:21
【问题描述】:

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

[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 属性为空会发生什么,因此将Name 设置为除null 之外的其他任何东西都不会很有帮助。

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

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

那是我预期的对象。 LockClass 是一个 DTO,只有上面一行中初始化的两个属性和强制性的 Equals 东西。
行动线应该是

controller.AddLockClass( lockClass.Id, null );

[SetUp] 创建所有模拟对象。我要测试的是,如果用户通过 GUI 手势(GUI 在控制器上调用 AddLockClass)创建了一个 LockClass 对象,其中名称为空,则控制器创建一个名称为空的对象。还有其他方法可以做到这一点,但现在必须这样。更改后的代码确实有效(例如 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();
    }
}

【问题讨论】:

  • controller.AddLockClass(lockClass.Id, string.Empty);
  • controller.AddLockClass() 的签名是什么?什么是会话工厂?什么是会话?它们是在哪里创建的?他们也是嘲笑吗?
  • 除了 LockClassPanelController 之外的所有东西都被模拟了(毕竟这是一个 unit 测试)。 AddLockClass(int id, string name)
  • sessionFactory 是一个创建会话(ISession、DB 访问会话、手动 DAL)的 ISessionFactory。不过没关系,反正都被嘲笑了。
  • 真的还是抛出同样的异常吗?你能粘贴你的 AddLockClass 方法的代码吗?

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


【解决方案1】:

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

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

这清楚的表明,如果你调用这个方法时带空名称参数,它在创建LockClass实例时仍然会使用空字符串。因此,正如您在问题的第一次编辑中注意到的那样,您在测试中应该期望的 LockClass 是:

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

and not 为 null 的版本。如果您需要进一步说明,请告诉我。

【讨论】:

  • ?? string.Empty 不应该在那里(我会编辑它)。请阅读我的各种编辑。测试的整个语义都是错误的(不是我想要测试的)。但是仍然很好奇为什么 rhino 会抛出这个异常。它不应该关心 Name 属性是否为空。
  • 这很简单。您设置期望 session.InsertWithId( lockClass ); LastCall.Return(lockClass); Rhino 完全期望这个调用,并将使用 Equals 方法比较参数(锁类)。这就是为什么它的 null 或空字符串很重要。我不确定你想如何在不比较参数的情况下对其进行测试。
  • 格泽尼奥是正确的。您设置了将返回特定 LockClass 的期望,但在您的方法中,您创建了 LockClass 的新实例。因此,测试失败。我不确定您为什么会遇到异常。我尝试使用 Rhino.Mocks 3.5 创建一个完整的测试,但测试失败,没有例外:pastebin.com/1Kb9ZxTK
  • LockClass 实现 Equals() 以便具有相同 id 和相同名称的两个实例被认为是相等的。您使用 MsTest 实现了测试。也许这是 NUnit 的问题。我将尝试创建一个隔离设置来重现异常。
【解决方案2】:

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

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

mockRepository.VerifyAll();

【讨论】:

  • 它仍然抛出同样的异常。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-18
  • 1970-01-01
  • 2011-01-10
相关资源
最近更新 更多