3

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);
jmzagorski
  • 1,035
  • 16
  • 38
  • 1
    In addition to the answers already given, I have to say I'm really enjoying one answer to [Best practice with unit testing abstract classes?](http://stackoverflow.com/a/2947823). Very good ideas in there. – Blair Conrad Dec 19 '14 at 21:38
  • I agree, I have read through some since I am starting to run into this problem. Thanks for the link. – jmzagorski Dec 19 '14 at 23:17
  • @BlairConrad I wasn't aware of that answer, very good indeed. It explains quite well why inheritance is, most of the time, a mistake (the "elephant in the room" of many OOP languages), and how it can be easily avoided. – rsenna Dec 22 '14 at 12:56

2 Answers2

2

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.

rsenna
  • 10,960
  • 1
  • 50
  • 60
  • 1
    Nice answer! I really like the concrete example of "Or you could have the base (common) functionality reside in a different method that's called by Validate. Then you could interrogate that method", that I lamely and slowly typed as you were posting yours. – Blair Conrad Dec 19 '14 at 21:21
  • @rsenna and @BlairConrad, thank you for the responses. I asked the question because I was using `Microsoft Fakes` and I knew you could code this behavior if you did not want to repeat base tests over in a concrete class. I can see your point about this being ugly and I would almost rather repeat the base tests for each child. – jmzagorski Dec 19 '14 at 23:12
1

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.

Community
  • 1
  • 1
Blair Conrad
  • 202,794
  • 24
  • 127
  • 110
  • I was looking over that post too and trying to work it in my example, coming up short of course. I do have access to production code and I will probably test base methods for each concrete test for now (or use `Microsoft Fakes` if it becomes too much) – jmzagorski Dec 19 '14 at 23:14