Mocking a private field

2019-05-18 10:44发布

问题:

I know a similar question has been asked but I have not found a clear solution. I'm trying to mock a private field from a large class. The private field gets instantiated in some earlier method and I'm trying to unit test a latter method which references the field.

So I have an earlier method in my class:

public bool validateAll(ref DataEntry[] oEntries, string sMediaPlanId, ITemplateGenerator oTempGen)
{
  ...
  // private field that I am trying to mock
  this._sMediaPlanObjective = (MPWrapper.Instance).getMediaPlanObjective(sMediaPlanId);
  ...
}

And I'm trying to Unit test a method that references the private field:

public bool validateFlightObjective(ref MPDataEntry oEntry)
{
  ...
  string entryFlightObjective = oEntry.getFlightObjective();
  string mediaPlanObjective = this._sMediaPlanObjective;

  if (entryFlightObjective != mediaPlanObjective)
  {
    return false;
  }
  ...

  return true;      
}

Given that I have a large class and this is just one method I want to test, is there a possible way to just mock this private field? Am I missing something basic or should I consider some other approach?

回答1:

You can't mock anything that's private, static, or essentially - non overridable (this comes as a free mocking libraries limitation).

What you usually do in such situations (when it appears that private member has to be tested), is extracting your private member to a separate class and inject it to tested class as a dependency.

In your case, you actually need to extract code that creates _sMediaPlanObjective, which is this line:

this._sMediaPlanObjective =
    (MPWrapper.Instance).getMediaPlanObjective(sMediaPlanId);

Object that provides getMediaPlanObjective method should be injected to your tested class. If you do so, you can simply mock that object and tell it to return mocked version of _sMediaPlanObjective.



回答2:

There is no reason to have any kind of tests on private fields.

using an object you can reference to the public methods as the object API. the object itself can have it's changing state according to the operation you perform on it - but it will be reflected in other public methods / access to DAL (DB / Registry / File/ any other resource that is not in memory)

So in your case you can have a unit tests like that:

call the method that initializes the private field as you expect it to and -

  • Call validateFlightObjective with a parameter that you know that have to return false according to the _sMediaPlanObjective "wanted to be state", and verify that the result is false.

  • Call validateFlightObjective with a parameter that you know that have to return true according to the _sMediaPlanObjective "wanted to be state", and verify that the result is true.

If you see that it is hard to test this object, then that might be a "smell" - maybe you have more than one responsibility there and you should start refactoring and split the class to smaller classes which will be easier to test

That it a bit long but i hope it was helpful



回答3:

You can use JustMock framework. For example:

double value = 0;
var fakeFilterSetHelper = Mock.Create<FilterSetHelper>(Behavior.CallOriginal);
Mock.NonPublic.Arrange<double>(fakeFilterSetHelper, memberName: "GetPriceRangeFromSession").Returns(value);