The 5 Stages Of Mocking

Thursday, February 17, 2005

When we write unit tests, we write a piece of test code to verify a piece of production code. The trouble is, production code has the nasty habit of relying on dependencies that are hard to control. Dependencies may be infrastructure dependencies like SMTP servers and databases, or may be software dependencies that are non-trivial to setup and get into the correct state for testing.

Mock objects remove these dependencies and simplify unit test code. A mock object is a stand-in replacement for a real-world domain object. For more details about mock objects, see “Mock Objects to the Rescue!”.

When I first began reading about mock objects, I did not like what I was reading. In fact, I disliked the idea so much, I entered ….

Stage 1: Denial

I found my eyes leaping over any pages containing the four letter m word. I felt confident in my gut reaction that m*ck objects were a fad –

- the same gut feeling I had when I heard of a new language by the name of C#.

As time passed, I realized the m*ck object talk was still picking up steam, and I entered ….

Stage 2: Resentment

I decided ‘object mockery’ was no longer a fad, but a placebo in source code form. Software development teams relying on mock objects would develop overconfidence in the quality of their product. These teams could watch their house of mock playing cards crumble in the cruel hands of the real world.

“Fools!”, my inner voice would yell in a Gandalf like tone. My inner voice always sounds like Gandalf or Ed Norton Jr. when I’m agitated.

As time went on, I began to write unit tests, and watch others write unit tests. Slowly … gradually … I entered ….

Stage 3: Bargaining

Some methods cry out for a unit test. Perhaps the method has logic to react to an SMTP error message, or perhaps the method has an exception handler to recuperate from a network error. These error conditions are difficult to trigger at just the right time, but I have to know exactly how the code will react. Unit tests are addictive that way. I found myself thinking about writing mock objects just to force these error conditions.

Suddenly, I was an object mocker.

The days grew long, and grey. One rainy night while sitting in my car - I hit bottom. I found myself tuning the radio to a country music station, and singing along to an old George Jones tune. I had officially entered …

Stage 4: Depression

I thought about the burden faced by the universe of software development. Any object I can think of will need to be mocked somewhere, in some program. Forget databases and mail servers. We will mock water fountains and pizza ovens. We will mock robot arms and booster engines. We will mock the stars and the sun. Yes, even the moon. The moon must be mocked.

Mock me, will you?

I’ll mock you, too….

I thought about starting a web site (Mock The World) and selling t-shirts, but I figured only lunatics and government agents would be interested and I didn’t follow through. It took some time to work out of my funk, but eventually, I reached …

Stage 5: A Wary Acceptance

I accept mock objects for what they are – a technique to simplify the creation of effective unit tests. I’m always worried, however, about assumptions. Production code contains assumptions about how other objects will behave. Mock objects have a high probability of encoding those same assumptions, and we end up testing tautologies.

 
Assert.IsTrue( mockWeather.RainTommorow || !mockWeather.RainTommorow). 

In software development, we always find a better way to build a mousetrap…

So, anyone using Mock Objects or NMock?


Comments
John Thursday, February 17, 2005
NMock looks pretty cool. I'm going to give it a run on a small project I'm working on at the moment.
<br>
<br>I know what you mean about that crappy feeling about mock vs. real world. I think you can rationalise it away with a statement such as &quot;Yeah.. so..? Unit testing doesn't take the place of integration testing, or beta testing, or user acceptance testing. It's still useful.&quot;
<br>
<br>John.
John Thursday, February 17, 2005
I couldn't help it.. :)
<br>
<br>John.
<br>
<br>
<br>
<br>using System;
<br>using System.Text;
<br>
<br>namespace com.odetocode.blogs {
<br>
<br> public interface IScottAllen : IEntity {
<br>
<br> void InteractWith( IEntity target );
<br>
<br> }
<br>
<br> public sealed class MockScottAllen : IScottAllen {
<br>
<br> void IScottAllen.InteractWith( IEntity target ) {
<br>
<br> if ( target is Woman ) {
<br> throw new ImAPussyException();
<br> }
<br> else if ( target is TheInternetTM ) {
<br> throw new ImABigPansyException();
<br> }
<br> else {
<br> throw new ImPoorlyImplementedException();
<br> }
<br> }
<br>
<br> void IEntity.HandleEngagement( IEntity from, EngagementMode mode, String remark ) {
<br>
<br> throw new OutOfMemoryException();
<br>
<br> }
<br> }
<br>
<br> public delegate void VoidDelegate();
<br>
<br> public sealed class TestRunner {
<br>
<br> public static void Main( String[] args ) {
<br>
<br> ScottAllenTestFixture fixture = new ScottAllenTestFixture();
<br>
<br> Try( new VoidDelegate( fixture.IsAPansy ), typeof( ImABigPansyException ) );
<br> Try( new VoidDelegate( fixture.IsAPussy ), typeof( ImAPussyException ) );
<br> Try( new VoidDelegate( fixture.IsNotAsAwesomeAsMe ), typeof( ImPoorlyImplementedException ) );
<br>
<br> Console.ReadLine();
<br>
<br> }
<br>
<br> private static void Try( VoidDelegate invoke, Type expectedException ) {
<br>
<br> try {
<br>
<br> invoke();
<br>
<br> }
<br> catch ( Exception ex ) {
<br>
<br> if ( expectedException == ex.GetType() ) {
<br>
<br> Console.WriteLine( &quot;Test passed with: {0}&quot;, ex.Message );
<br>
<br> }
<br> else {
<br>
<br> Console.WriteLine( &quot;Unexpected exception: {0}&quot;, ex.Message );
<br>
<br> }
<br> }
<br> }
<br> }
<br>
<br>
<br> public sealed class ScottAllenTestFixture {
<br>
<br> public void IsAPansy() {
<br>
<br> IScottAllen scott = new MockScottAllen();
<br> scott.InteractWith( TheInternetTM.GetInstance() );
<br>
<br> }
<br>
<br> public void IsAPussy() {
<br>
<br> IScottAllen scott = new MockScottAllen();
<br> scott.InteractWith( new Woman( &quot;Julie&quot; ) );
<br>
<br> }
<br>
<br> public void IsNotAsAwesomeAsMe() {
<br>
<br> IScottAllen scott = new MockScottAllen();
<br> scott.InteractWith( JohnElliot.TheOneAndOnly );
<br>
<br> }
<br> }
<br>
<br> public interface IEntity {
<br>
<br> void HandleEngagement( IEntity from, EngagementMode mode, String remark );
<br>
<br> }
<br>
<br> public sealed class Woman : IEntity {
<br>
<br> private String _name;
<br>
<br> public Woman( String name )
<br> : base() {
<br>
<br> this._name = name;
<br>
<br> }
<br>
<br> void IEntity.HandleEngagement( IEntity from, EngagementMode mode, String remark ) {
<br>
<br> if ( from is IScottAllen ) {
<br>
<br> this.EngageScottAllen( (IScottAllen)from );
<br>
<br> }
<br> }
<br>
<br> private void EngageScottAllen( IScottAllen scott ) {
<br>
<br> scott.HandleEngagement( this, EngagementMode.Condescending, &quot;Suzzy told me it wasn't very big anyway...&quot; );
<br>
<br> }
<br> }
<br>
<br> public sealed class TheInternetTM : IEntity {
<br>
<br> private static TheInternetTM _instance = new TheInternetTM();
<br> private TheInternetTM() {}
<br>
<br> public static TheInternetTM GetInstance() { return _instance; }
<br>
<br> void IEntity.HandleEngagement( IEntity from, EngagementMode mode, String remark ) {
<br>
<br> if ( from is IScottAllen ) {
<br>
<br> this.EngageScottAllen( (IScottAllen)from );
<br>
<br> }
<br> }
<br>
<br> private void EngageScottAllen( IScottAllen scott ) {
<br>
<br> scott.HandleEngagement( this, EngagementMode.Disinterested, null );
<br>
<br> }
<br> }
<br>
<br> public abstract class JohnElliot : IEntity, IDisposable {
<br>
<br> private static JohnElliot _jj5 = new JohnElliot.DJ_jj5();
<br>
<br> private JohnElliot() {}
<br>
<br> public static JohnElliot TheOneAndOnly { get { return _jj5; } }
<br>
<br> void IDisposable.Dispose() {
<br>
<br> throw new InvalidOperationException( &quot;You are a ballbag.&quot; );
<br>
<br> }
<br>
<br> void IEntity.HandleEngagement( IEntity from, EngagementMode mode, String remark ) {
<br>
<br> from.HandleEngagement( from, EngagementMode.TooCool, &quot;I'm awesome!&quot; );
<br>
<br> }
<br>
<br> private sealed class DJ_jj5 : JohnElliot {
<br>
<br> public void DoAwesomeSecretStuff() {
<br>
<br> // classified.
<br>
<br> }
<br> }
<br> }
<br>
<br> public enum EngagementMode {
<br> Friendly,
<br> Counteous,
<br> Rude,
<br> Disinterested,
<br> Condescending,
<br> TooCool,
<br> }
<br>
<br> public class ImABigPansyException : ApplicationException {
<br> public ImABigPansyException()
<br> : base( &quot;I'm scared. I think my implementor must have made me a big pansy.&quot; ) {}
<br> }
<br>
<br> public class ImAPussyException : ApplicationException {
<br> public ImAPussyException()
<br> : base( &quot;My mummy told me that girls have boobies not little willies like me.&quot; ) {}
<br> }
<br>
<br> public class ImPoorlyImplementedException : ApplicationException {
<br> public ImPoorlyImplementedException()
<br> : base( &quot;LSP? LSP!? ...never heard of it!?&quot; ) {}
<br> }
<br>}
Scott Thursday, February 17, 2005
Et tu, John?
<br>Remind me if I get to Austrailia I have to kick your - ass *before* you buy me a drink. :)
John Thursday, February 17, 2005
Sure thing!
<br>
<br>But might I advise that you get me to buy your beer *before* you kick my *arse*..? That way you can really go to town on me, and still get your beer!
<br>
<br>:)
<br>
<br>p.s. Did you notice how horribly flawed my tests are..? If they fail, they don't, um, fail.. there's your tautology right there! :P
<br>
<br>p.p.s. Are you coming to TechEd in Aus later in the year..?
Scott Thursday, February 17, 2005
I'd love to get to AUS for any reason but, so far it's not in the plans. Something like a TechEd would be pretty cool.
<br>
<br>I wish we had teleportation devices instead of giant flying tubes packed tightly with humans....
Haacked Thursday, February 17, 2005
Often I'll replace the mock with the real thing whenever possible. For example, I have code for a simple SMTP server that I can query for messages after I send the message. I also use Cassini when I need to test an HTTP post.
<br>
<br>If I can use the &quot;real&quot; thing, I prefer that to using Mock. Mock is sort of a last resort when I'm doing interaction testing.
Comments are now closed.
by K. Scott Allen K.Scott Allen
My Pluralsight Courses
The Podcast!