views:

936

answers:

7

I'm really new to mocks and am trying to replace a private field with a mock object. Currently the instance of the private field is created in the constructor. My code looks like...

public class Cache {
    private ISnapshot _lastest_snapshot;

    public ISnapshot LatestSnapshot {
        get { return this._lastest_snapshot; }
        private set { this._latest_snapshot = value; }
    }

    public Cache() {
        this.LatestSnapshot = new Snapshot();
    }

    public void Freeze(IUpdates Updates) {
        ISnapshot _next = this.LastestSnapshot.CreateNext();
        _next.FreezeFrom(Updates);
        this.LastestSnapshot = _next;
    }

}

What I'm trying to do is create a unit test that asserts ISnapshot.FreezeFrom(IUpdates) is called from within Cache.Freeze(IUpdates). I'm guessing I should replace the private field _latest_snapshot with a mock object (maybe wrong assumption?). How would I go about that while still retaining a parameterless constructor and not resorting to making LatestSnapshot's set public?

If I'm totally going about writing the test the wrong way then please do point out as well.

The actual implementation of ISnapshot.FreezeFrom itself calls a heirarchy of other methods with a deep object graph so I'm not too keen on asserting the object graph.

Thanks in advance.

+3  A: 

I don't think you'd need to mock private member variables. Isn't the whole idea of mocking that the public interface for an object works as expected? Private variables are implementation details that mocks aren't concerned with.

Jason
+2  A: 

I'm not sure that you can do that. If you're wanting to test _next then you're probably going to have to pass it in as a parameter and then in your unit test pass in a Mock object which you can then test using an Expectation. That's what I'd be doing if I were trying to do it in Moq.

As an example of what I might try using the Moq framework:

Mock<ISnapshot> snapshotMock = new Mock<ISnapshot>();
snapshotMock.Expect(p => p.FreezeFrom(expectedUpdate)).AtMostOnce();
Cache c = new Cache(snapshotMock.Object);
c.Freeze(expectedUpdate);

Note: I haven't tried to compile the above code. Its just there to give an example of how I'd approach solving this.

mezoid
+6  A: 

I'm almost citing techniques from "Working Effectively with Legacy Code":

  1. Sub-class your class in a unit test and supersede your private variable with a mock object in it (by adding a public setter or in the constructor). You probably have to make the variable protected.
  2. Make a protected getter for this private variable, and override it in testing subclass to return a mock object instead of the actual private variable.
  3. Create a protected factory method for creating ISnapshot object, and override it in testing subclass to return an instance of a mock object instead of the real one. This way the constructor will get the right value from the start.
  4. Parametrize constructor to take an instance of ISnapshot.
Alex B
+1  A: 

This answer might be simple, but looking at the code, is there any way in which ISnapshot.FreezeFrom(IUpdates) is not going to be called? Sounds like you want to assert something that will always be true.

As Jason says, mocking is meant for situations where your class depends on SomeInterface to do it's work, and you want to test YourClass in isolation from whichever implementation of SomeInterface you actually use at runtime.

matt b
if you like it, up it :)
Jason
+1  A: 

The question to ask is: what are the externally visible effects if this worked?

What happens to all those Snapshots? One option might to initialise the Cache with its first Snapshot from outside, say in the constructor. Another might be to mock whatever it is that the Snapshot calls that matters outside the cache. It depends on what you care that happens.

Steve Freeman
A: 

It might be too late to respond. Anyways. I also had similar problem.

public class Model
{
  public ISomeClass XYZ{
      get;
      private set;
      }
}

I required to set value of XYZ in my test case. I resolved the problem using this syntex.

Expect.Call(_model.XYZ).Return(new SomeClass());
_repository.ReplayAll();

In the case above we can do it like this

Expect.Call(_cache.LatestSnapshot).Return(new Snapshot());
_repository.ReplayAll();
A: 

Turn Cache into a template as shown below.

template public class Cache { private T _lastest_snapshot;

public T LatestSnapshot {
    get { return this._lastest_snapshot; }
    private set { this._latest_snapshot = value; }
}

public Cache() {
    this.LatestSnapshot = new Snapshot();
}

public void Freeze(IUpdates Updates) {
    T _next = this.LastestSnapshot.CreateNext();
    _next.FreezeFrom(Updates);
    this.LastestSnapshot = _next;
}

}

In production code do:

Cache<> foo;//OR Cache bar;

In test code do: Cache mockFoo;

Dawit