future feature thought

1 view
Skip to first unread message

David Chelimsky

unread,
Dec 19, 2005, 11:45:46 AM12/19/05
to Rhino...@googlegroups.com
Hi fellow rhino-ers,

One thing I notice is that all of my tests that don't use Rhino mocks
have this structure:

[Test] public void SomethingShouldDoSomething()
{
//build
//operate
//check state
}

and my tests that do use Rhino have this structure:

[Test]public void SomethingShouldDoSomething()
{
//build
//replay
//operate
//verify
}

Having these four distinct parts of each test is a little bit
confusing unless you comment them as such (and I believe in "comments
lie, so don't use them"). I'm experimenting w/ something that ends up
looking like this:

[Test]public void SomethingShouldDoSomething()
{
using (SetExpectations.Do(mocks))
{
//build
}
using (Execute.Do(mocks))
{
//operate
}
}

The two using blocks result in a call to mocks.ReplayAll() and
mocks.VerifyAll(). I realize that you just took pains to remove these,
but I wonder if anyone else sees this as useful, has any naming
recommendations, etc.

Someday, C# will support blocks and we'll be able to do this:

[Test]public void SomethingShouldDoSomething()
{
SetExpectations
{
//build
}
Execute
{
//operate
}
}

That seems more clear. Someday. But in the mean time....

Ayende Rahien

unread,
Dec 19, 2005, 2:54:33 PM12/19/05
to Rhino...@googlegroups.com
The problem with using using() is that it masks exceptions that occurs.
It's great when you're not having anything unexpected happen, but when it
does....

I recommend that you would do something like:

[Test]public void SomethingShouldDoSomething() {
//build
//replay
//operate
}

[TearDown] public void TearDown() {

mocks.VerifyAll();

Jeff Brown

unread,
Dec 19, 2005, 2:54:10 PM12/19/05
to Rhino...@googlegroups.com
This seems to use more code than the alternative. It's also going to be
susceptible to the same kind of problem with exceptions thrown by
VerifyAll masking the ones you really want to see.

Anonymous delegates wouldn't have the same problem as "using" but then
it would be even more verbose. It's almost too bad that running code
can't ask whether an exception is currently being propagated. That
would solve this problem neatly (but perhaps introduce others...).

Of course, with the new MockRepository and VerifyAll in the base-class,
all you're really getting out of this is a ReplayAll and some visual
isolation, which isn't always what you want anyways. Doesn't seem to
buy much more than comments do.

Jeff.

Ayende Rahien

unread,
Dec 19, 2005, 3:06:05 PM12/19/05
to Rhino...@googlegroups.com
Indeed, and I would like to say that visual isolation can be _very_ bad at
times. You forget something because it's not in front of you and it turn
back to bite you, usually three weeks later.

David Chelimsky

unread,
Dec 20, 2005, 7:20:54 AM12/20/05
to Rhino...@googlegroups.com
On 12/19/05, Ayende Rahien <aye...@gmail.com> wrote:
>
> The problem with using using() is that it masks exceptions that occurs.
> It's great when you're not having anything unexpected happen, but when it
> does....
I implemented this using using() and got to see all my exceptions in
the right places:

public class SetExpectations : IDisposable
{
private MockRepository mocks;
public SetExpectations(MockRepository mocks)
{ this.mocks = mocks; }
public void Dispose()
{ mocks.ReplayAll(); }
}

public class RunTest : IDisposable
{
private MockRepository mocks;
public RunTest(MockRepository mocks)
{ this.mocks = mocks; }
public void Dispose()
{ mocks.VerifyAll(); }
}

[Test] public void TestSomething()
{
using (new SetExpectations(mocks))
{ //build }
using (new RunTest(mocks))
{ //operate }
}

So I think it may depend on where/how you implement things. If you
were to use the above classes (with better names) and implement
methods in MockRepository that returned them, you could get this:

[Test] public void TestSomething()
{
using (mocks.SetExpectations())
{ //build }
using (mocks.RunTest())
{ //operate }
}

Now I'm not saying that this is ideal - but I think it's doable. The
question is not IF we can do it - the question I have is "does anyone
like the idea?"

David Chelimsky

unread,
Dec 20, 2005, 7:24:15 AM12/20/05
to Rhino...@googlegroups.com
On 12/19/05, Jeff Brown <Je...@ingenio.com> wrote:
> Doesn't seem to
> buy much more than comments do.
The problem w/ comments is that people change code without changing
the comments. Since they don't compile with the code, this doesn't get
caught until some poor developer is trying to understand a piece of
code by reading a bunch of comments that are lying to him. As a
general rule, if I see a comment, I want to replace it with a method
call. That's what leads me to this idea.

David Chelimsky

unread,
Dec 20, 2005, 7:27:24 AM12/20/05
to Rhino...@googlegroups.com
On 12/19/05, Ayende Rahien <aye...@gmail.com> wrote:
> Indeed, and I would like to say that visual isolation can be _very_ bad at
> times. You forget something because it's not in front of you and it turn
> back to bite you, usually three weeks later.
I'm not sure what you mean here. When you say "visual isolation" do
you mean implicit operations, or operations that are defined elsewhere
(like in the setup or teardown method)? Or do you mean separating the
test method into little visual subsections like this:

[Test] public void TestSomething()
{
Expect.Call(mockA.doSomething(x,y)).Return(z);

mocks.ReplayAll();

realB.doSomething(e,f);

mocks.VerifyAll();
}

Ayende Rahien

unread,
Dec 20, 2005, 7:29:31 AM12/20/05
to Rhino...@googlegroups.com
Try to see what happens if you've something like

SetExpectations(mocks)
{
  obj.MethodThatReturnSomething();
  throw new Exception();
}//You get the wrong message here.

Ayende Rahien

unread,
Dec 20, 2005, 7:37:53 AM12/20/05
to Rhino...@googlegroups.com
I mean things that are happening that aren't in your face (setup/teardown are acceptable)

On 12/20/05, David Chelimsky < dchel...@gmail.com> wrote:

David Chelimsky

unread,
Dec 20, 2005, 7:54:13 AM12/20/05
to Rhino...@googlegroups.com
On 12/20/05, Ayende Rahien <aye...@gmail.com> wrote:
> Try to see what happens if you've something like
>
> SetExpectations(mocks)
> {
> obj.MethodThatReturnSomething();
> throw new Exception();
> }//You get the wrong message here.

Here's my experience (w/ the exact code copied from VS):

=============================
using System;
using NUnit.Framework;
using Rhino.Mocks;

namespace Something
{
[TestFixture]
public class SomethingTest
{
MockRepository mocks;

[SetUp]
public void SetUp()
{
mocks = new MockRepository();
}

[Test]
public void ThrowExceptionInBlock()
{
using (new SetExpectations(mocks))
{
throw new ApplicationException("exception thrown in test method");
}
}

[Test]
public void ThrowExceptionFromMethodCall()
{
using (new SetExpectations(mocks))
{
ExceptionThrowingClass exceptionThrowingClass = new
ExceptionThrowingClass();
exceptionThrowingClass.ExceptionThrowingMethod();
}
}
}

public class SetExpectations : IDisposable
{
private MockRepository mocks;

public SetExpectations(MockRepository mocks)
{
this.mocks = mocks;
}

public void Dispose()
{
mocks.ReplayAll();
}
}

public class ExceptionThrowingClass
{
public void ExceptionThrowingMethod()
{
throw new ApplicationException("exception thrown by class under test");
}
}
}
=========================
------ Test started: Assembly: ClassLibrary1.dll ------

TestCase 'Something.SomethingTest.ThrowExceptionInBlock' failed:
System.ApplicationException : exception thrown in test method
c:\projects\dotnet\solution1\classlibrary1\whatever.cs(23,0): at
Something.SomethingTest.ThrowExceptionInBlock()

TestCase 'Something.SomethingTest.ThrowExceptionFromMethodCall'
failed: System.ApplicationException : exception thrown by class under
test
c:\projects\dotnet\solution1\classlibrary1\whatever.cs(57,0): at
Something.ExceptionThrowingClass.ExceptionThrowingMethod()
c:\projects\dotnet\solution1\classlibrary1\whatever.cs(33,0): at
Something.SomethingTest.ThrowExceptionFromMethodCall()


0 succeeded, 2 failed, 0 skipped, took 0.03 seconds.

---------------------- Done ----------------------
==========================

This is exactly the feedback you would want. It points directly to the
lines throwing the exceptions in both cases.

So it's doable. The question is whether it's usable. Thoughts on that?

David Chelimsky

unread,
Dec 20, 2005, 7:55:42 AM12/20/05
to Rhino...@googlegroups.com
On 12/20/05, Ayende Rahien <aye...@gmail.com> wrote:
> I mean things that are happening that aren't in your face (setup/teardown
> are acceptable)
> > > Indeed, and I would like to say that visual isolation can be _very_ bad
> at
> > > times.
Agree 100%. I used to thing that implicit was cool. I have to many
bruises from that line of thinking.

Ayende Rahien

unread,
Dec 20, 2005, 7:58:29 AM12/20/05
to Rhino...@googlegroups.com
No, the question is whatever an exception thrown from inside the using will interfer with it.

Try to throw an exception from the VerifyExpectations where there are outstanding uncalled methods.

On 12/20/05, David Chelimsky <dchel...@gmail.com> wrote:

David Chelimsky

unread,
Dec 20, 2005, 8:18:29 AM12/20/05
to Rhino...@googlegroups.com
On 12/20/05, Ayende Rahien <aye...@gmail.com> wrote:
> No, the question is whatever an exception thrown from inside the using will
> interfer with it.
>
> Try to throw an exception from the VerifyExpectations where there are
> outstanding uncalled methods.

OK - I *think* this is what you mean - please tell me if I'm missing something:

====================================


using System;
using NUnit.Framework;
using Rhino.Mocks;

namespace Something
{
[TestFixture]
public class SomethingTest
{
MockRepository mocks;

Collaborator collaborator;

[SetUp]
public void SetUp()
{
mocks = new MockRepository();

collaborator = (Collaborator)mocks.CreateMock(typeof(Collaborator));
}

[Test] public void ReturnIsMissing()
{
using (new SetExpectations(mocks))
{
collaborator.SomeMethodThatReturnsBool();
}
}

[Test]
public void SecondMethodNotCalled()
{
using (new SetExpectations(mocks))
{
Expect.Call(collaborator.SomeMethodThatReturnsBool()).Return(true);
}
using (new VerifyExpectations(mocks))
{
throw new ApplicationException("thrown before expectation could be met");
}
}
}

public class SetExpectations : IDisposable
{
private MockRepository mocks;

public SetExpectations(MockRepository mocks)
{
this.mocks = mocks;
}

public void Dispose()
{
mocks.ReplayAll();
}
}

public class VerifyExpectations : IDisposable
{
private MockRepository mocks;

public VerifyExpectations(MockRepository mocks)
{
this.mocks = mocks;
}

public void Dispose()
{
mocks.VerifyAll();
}
}

public interface Collaborator
{
bool SomeMethodThatReturnsBool();
}
}
==============================================


------ Test started: Assembly: ClassLibrary1.dll ------

TestCase 'Something.SomethingTest.ReturnIsMissing' failed:
System.InvalidOperationException : Previous method
'Collaborator.SomeMethodThatReturnsBool();' require a return value or
an exception to throw.
at Rhino.Mocks.Impl.RecordMockState.PreviousMethodIsClose()
at Rhino.Mocks.Impl.RecordMockState.Replay()
at Rhino.Mocks.MockRepository.Replay(Object obj)
at Rhino.Mocks.MockRepository.ReplayAll()
c:\projects\dotnet\solution1\classlibrary1\whatever.cs(53,0): at
Something.SetExpectations.Dispose()
c:\projects\dotnet\solution1\classlibrary1\whatever.cs(24,0): at
Something.SomethingTest.ReturnIsMissing()

TestCase 'Something.SomethingTest.SecondMethodNotCalled' failed:
Rhino.Mocks.Exceptions.ExpectationViolationException :
Collaborator.SomeMethodThatReturnsBool(); Expected #1, Actual #0.
at Rhino.Mocks.Impl.ReplayMockState.Verify()
at Rhino.Mocks.MockRepository.Verify(Object obj)
at Rhino.Mocks.MockRepository.VerifyAll()
c:\projects\dotnet\solution1\classlibrary1\whatever.cs(68,0): at
Something.VerifyExpectations.Dispose()
c:\projects\dotnet\solution1\classlibrary1\whatever.cs(37,0): at
Something.SomethingTest.SecondMethodNotCalled()


0 succeeded, 2 failed, 0 skipped, took 0.11 seconds.

---------------------- Done ----------------------

Ayende Rahien

unread,
Dec 20, 2005, 8:25:29 AM12/20/05
to Rhino...@googlegroups.com
Yes, the first time it's should be:


 [Test] public void ReturnIsMissing()
               {
                       using (new SetExpectations(mocks))
                       {
                                collaborator.SomeMethodThatRet
urnsBool();
throw new Exception("You won't see me");

                       }
               }

The issue is of swallowing exceptions.

On 12/20/05, David Chelimsky <dchel...@gmail.com> wrote:
'Collaborator.SomeMethodThatReturnsBool ();' require a return value or

an exception to throw.
        at Rhino.Mocks.Impl.RecordMockState.PreviousMethodIsClose()
        at Rhino.Mocks.Impl.RecordMockState.Replay()
        at Rhino.Mocks.MockRepository.Replay (Object obj)

David Chelimsky

unread,
Dec 20, 2005, 8:35:05 AM12/20/05
to Rhino...@googlegroups.com
THANK YOU! Now I understand. Thanks for your patience.

Cheers,
David

Reply all
Reply to author
Forward
0 new messages