How do I mock a private field?

匿名 (未验证) 提交于 2019-12-03 02:56:01

问题:

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.

回答1:

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.


回答2:

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.



回答3:

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.



回答4:

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.



回答5:

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.



回答6:

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();


回答7:

You will probably have to refactor your class like this, in order for it to be injected with a different dependency for ISnapshot. Your class will remain to function the same.

public class Cache {  private ISnapshot _lastest_snapshot;   public ISnapshot LatestSnapshot {   get { return this._lastest_snapshot; }   private set { this._latest_snapshot = value; }  }   public Cache() : this (new Snapshot()) {  }   public Cache(ISnapshot latestSnapshot) {   this.LatestSnapshot = latestSnapshot;  }   public void Freeze(IUpdates Updates) {   ISnapshot _next = this.LastestSnapshot.CreateNext();   _next.FreezeFrom(Updates);   this.LastestSnapshot = _next;  }  }


回答8:

You can simply add "setSnapshot(ISnapshot)" method to the Cache with your mocked class instance.

You can also add a constructor that takes ISnapshot.



回答9:

Turn Cache into a template as shown below.

template <typename T=ISnapshot> 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<ISnapshot> bar;

In test code do:

Cache<MockSnapshot> mockFoo;


转载请标明出处:How do I mock a private field?
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!