0

I currently have a base service class that all my services extend. This is what one of the methods look like:

   protected internal virtual T PerformServiceOperationWithExceptionHandling<T>(Func<T> func)
        {
            try
            {
                return func.Invoke();
            }

            ...
        }

In the derived classes I call the method like this:

    public AddGuestResponse AddGuest(AddGuestRequest addGuestRequest)
    {
        return PerformServiceOperationWithExceptionHandling(() => AddGuestLogic(addGuestRequest));
    }

I want to test AddGuest and ensure "AddGuestLogic" is being passed as a parameter in the base method? How do I achieve this with nSubstitute and nUnit. I don't think its possible?

================================================

I ended up using the following code:

    [Test]
    public void AddGuest_WhenCalled_PerformsAddGuestLogicWithExceptionHandling()
    {
        Func<AddGuestResponse> addGuestLogic = null;
        _guestService.PerformServiceOperationWithExceptionHandling(Arg.Do<Func<AddGuestResponse>>(arg => addGuestLogic = arg));
        var addGuestRequest = new AddGuestRequest();
        _guestService.AddGuest(addGuestRequest);
        _guestService.ClearReceivedCalls();

        addGuestLogic.Invoke();

        _guestService.Received().AddGuestLogic(addGuestRequest);
    }

The _guestService is created in my setup method as follows: Substitute.ForPartsOf();

Cool Breeze
  • 1,289
  • 11
  • 34

2 Answers2

2

I second Sunny Milenov's answer, but would go one step further by advising you to change your design. I have learned the hard way that many of these headaches with testing base class behavior go away when you follow the principle of composition over inheritance.

I.e., if you refactor your base class to a collaborator, which you inject into your services' constructor, you can test that in isolation and mock it in your services' tests. No worrying about testing an abstract base class or testing the same exception handling in all of your services' tests.

You would test that the collaborator correctly invokes the func in the collaborator's tests.

In the services' tests you can just mock the collaborator to return the Func's result right away:

 [Test]
 public void ServiceLogicIsExecuted()
 {
     var collaborator = Substitute.For<ICollaborator>();

     //Tell the test double to return the Func's result. You'd probably want to do this in the setup method.
     collaborator.PerformServiceOperation(Arg.Any<Func<int>>()).Returns(x => ((Func<int>)x[0]).Invoke());

     var sut = new Service(collaborator);

     var result = sut.CalculateSomething();

     Assert.That(result, Is.EqualTo(99));
 }

 public class Service
 {
     private readonly ICollaborator _collaborator;

     public Service(ICollaborator collaborator)
     {
         _collaborator = collaborator;
     }

     public int CalculateSomething()
     {
         return _collaborator.PerformServiceOperation(ExecuteLogic);
     }

     private static int ExecuteLogic()
     {
         return 99;
     }
 }

 public interface ICollaborator
 {
     T PerformServiceOperation<T>(Func<T> func);
 }
EagleBeak
  • 6,939
  • 8
  • 31
  • 47
  • The problem I am having is that I cannot test to see if my base class method received the correct lambda expression. Even if I were to change it to favor composition over inheritance (something I did consider) I still have the same issue i.e. because the two lambda expressions even though are the same will be pointing to two different memory locations. – Cool Breeze Mar 12 '15 at 02:20
  • I also agree with Sunny if I understand him/her correctly... i.e. to favor state based testing over interaction based testing? Although I feel by doing this I am testing the same exception handling in all of my services tests. – Cool Breeze Mar 12 '15 at 02:21
  • I added an example for a service test showing how you can ensure that your collaborator test double executes the service's logic, which is passed to it as a Func. I don't think Sunny meant to discredit interaction based testing. His point is that testing a base class in isolation is of little use, because the base class's behavior will always be executed interacting with a derived class's logic. – EagleBeak Mar 12 '15 at 09:01
  • Thank you for the code example :) I ended up sticking with inheritance for now even though your solution is better. I achieved my goal as outlined in the blog post here: https://journeymanyears.wordpress.com/2011/12/10/unit-testing-lambda-expressions/ – Cool Breeze Mar 12 '15 at 10:47
  • No problem! Another approach could be to add a testing hook to your base class, I.e., a getter for the Func, which you only use for testing. I think that's an OK compromise, but many people disagree strongly with adding this kind of hook to production code. – EagleBeak Mar 12 '15 at 10:54
  • I just updated my original post to include the code I actually used. To me it seems pretty clean? – Cool Breeze Mar 12 '15 at 10:58
  • I find that OK for a situation where you can't modify the design further. (You still had to make your exception handling public to fiddle with it with NSubstitute, right?) My two main concerns with "your" approach would be that firstly you're testing implementation details. Your test interacts with 3 methods of the service, two of which should actually be private (or protected). Secondly I find the mocking setup in the test more difficult to understand than necessary. But hey - I've seen much much worse... – EagleBeak Mar 12 '15 at 14:24
1

Short answer - you shouldn't. Unit testing is about testing the behavior of the tested method, not the implementation details.

Long answer: It doesn't matter how the class internally works, as far as it produces the expected results.

You need to test your public method on the final class and see if this works as expected. Testing a base/abstract class in isolation proves nothing.

Sunny Milenov
  • 21,990
  • 6
  • 80
  • 106