为什么 RhinoMocks 在我的测试中抛出 InvalidOperationException?
这是我目前正在进行的测试:
(根据 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
属性为 null 会发生什么,因此将 Name
设为除 null
以外的任何其他值都不会很有帮助。
编辑:
该代码实际上没有测试我想测试的内容。该方法的第一行应该是
LockClass lockClass = new LockClass { Id = ValidId, Name = string.Empty };
Thats myected object。 LockClass
是一个 DTO,仅包含上面一行中初始化的两个属性以及必需的 Equals
内容。
行为行应该是
controller.AddLockClass( lockClass.Id, null );
[SetUp] 创建所有模拟对象。我想要测试的是,如果用户通过 GUI 手势创建一个 LockClass 对象(GUI 在控制器上调用 AddLockClass
),其中名称为 null,则控制器会创建一个名称为空的对象。还有其他方法可以做到这一点,但现在必须采用这种方式。更改后的代码确实有效(例如,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();
}
}
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();
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我认为您需要将测试的“act”部分包含在播放范围内:
I think you need to enclose the 'act' part of the test inside a playback scope:
Rhino Mocks 为您提供正确的异常。在 AddLockClass 中,您有以下行:
它清楚地表明,如果您使用 null 名称参数调用此方法,则在创建 LockClass 实例时,它仍将使用空字符串。因此,正如您在问题的第一次编辑中注意到的那样,您在测试中应该期望的 LockClass 是:
而不是带有 null 的版本。如果您需要进一步说明,请告诉我。
Rhino Mocks gives you correct exception. In your AddLockClass you have the following line:
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:
and not the version with null. Let me know if you need further clarification.