FakeItEasy, Fake a parents virtual method from the child class

2k Views Asked by At

I am trying to fake a call to a parents public virtual validate method from the child without success (using FakeItEasy. I have a base class that validates simple commands for similar command classes (I have reduced the code for simplicity sake):

  public class CanSetSettings<TSettings> : IValidationhandler<TSettings> where TSettings : ISetting
  {
    protected readonly IFooRepository Repository;
    protected List<ValidationResult> Results;

    public CanSetSettings(IFooRepository repository)
    {
      if (Repository== null)
        throw new ArgumentNullException("IFooRepository ", "IFooRepository is needed to validate the command.");

      repository = repository;
      Results = new List<ValidationResult>();
    }

    public virtual ICollection<ValidationResult> Validate(TSettings settings)
    {
      if (settings == null)
      {
        Results.Add(new ValidationResult("Settings", "The command to validate cannot be missing."));
        return Results;
      }

      if (Repository.Query(settings.Location) == null)
        Results.Add(new ValidationResult("Location", "No Location was found for your settings."));

      return Results;
    }

Then I have child classes that inherits from this base class and then implements their specific logic by overriding Validate (simplified my code).

public class CanSetSpecificSetting : CanSetSettings<SetSpecificSettings>, IValidationhandler<SetSpecificSettings>
  {
    public CanSetSpecificSetting (IFooRepository repo)
      : base(repo)
    { }

    public override ICollection<ValidationResult> Validate(SetSpecificSettings command)
    {
      base.Validate(command); // TODO determine if this call was made

    // ... other logic here

      return results;
    }
  }

I have tried this in my unit test and it only configures the method call to the child class and I cannot configure the parent. How do I configure the fake to call the child class and fake the parent base class method? Thank you.

      var _repository = A.Fake<IFooRepository>();
      var _validator = A.Fake<CanSetSpecificSetting>(opt => opt.WithArgumentsForConstructor(new object[] { _repository }));

      A.CallTo(() => _validator.Validate(_cmd)).CallsBaseMethod().Once();

      _validator.Validate(_cmd);

      // this passes, but only because it knows about the child call
      A.CallTo(() => _validator.Validate(_cmd)).MustHaveHappened(Repeated.Exactly.Once);
2

There are 2 best solutions below

2
On

No, I don't think you can easily do what you intend to do using FakeItEasy. I don't even think you should do that.

BUT you can achieve something similar, by encapsulating the base call into a template method in your subclass. Just change CanSetSpecificSetting, like this:

public class CanSetSpecificSetting : CanSetSettings<SetSpecificSettings>, IValidationhandler<SetSpecificSettings>
{
    public CanSetSpecificSetting (IFooRepository repo)
    : base(repo)
    { }

    public override ICollection<ValidationResult> Validate(SetSpecificSettings command)
    {
        BaseValidate(command); // << Instead of calling base.Validate(command).

        // ... other logic here

        return results;
    }

    // This is the template method. You MUST declare it as virtual.
    protected virtual ICollection<ValidationResult> BaseValidate(SetSpecificSettings command)
    {
        return base.Validate(command);
    }
}

And then change your test like this:

var _repository = A.Fake<IFooRepository>();
var _validator = A.Fake<CanSetSpecificSetting>(opt => opt.WithArgumentsForConstructor(new object[] { _repository }));

A.CallTo(() => _validator.Validate(_cmd)).CallsBaseMethod().Once();

// *** ADD THIS LINE *** must configure it this way because the template method is protected - we don't want to make it public!
A.CallTo(_validator).WhereMethod(x => x.Name == "BaseValidate").Returns("whatever-you-want-to-return");

_validator.Validate(_cmd);

A.CallTo(() => _validator.Validate(_cmd)).MustHaveHappened(Repeated.Exactly.Once);

Again, this is ugly. The only case I can imagine that doing this would be ok is adding tests to some legacy code that you intend (and will) refactor as soon as possible.

Hope it helps.

1
On

Interesting question. A variation on Use FakeItEasy's A.CallTo() on another method in same object, except it's not "another method in the same object" so much as it is "(nearly) the same method in the same object".

I don't think this is possible. When FakeItEasy makes its fake, it creates a subclass that has its own definition of Validate. We can interrogate that method, but there is no way to modify the behaviour of any of the base classes, and so we can't ask the class whether its base was called.

I think this approach to testing the validators is a little unusual, and would recommend a different path. If you have the option to restructure the production code, you could compose validators instead of defining them in a hierarchy. Or you could have the base (common) functionality reside in a different method that's called by Validate. Then you could interrogate that method, although I don't recommend this latter approach.

If you can't (or prefer not to) restructure the production code, an alternative would be to test the overall behaviour of the concrete validators. Rather than seeing if CanSetSpecificSetting calls its base, consider checking to see if it behaves properly under different conditions, for example when the repository is null. Not only is this easier (possible) to verify, the approach would give better results—ensuring that your entire system behaves the way you want it to, not just that some methods certain calls to their parent.

Depending on your testing framework, it can be quite easy to generate all these tests by parameterizing the base test cases and then executing them for every concrete implementation.