MicroMan
MicroMan

Reputation: 2098

Test a recursive Function with Moq

I have a recursive method i have tried to simply it for the sake of readability as it is a messy method.

I would like to get 100% coverage of the recursive method. I am struggling to setup the mock that will go through the recursion twice. then exit. I think i would need to use call back and not returns, I am not 100% sure.

    [TestMethod]
    public void TestRecursiveLoop()
    {
      var rootList = new List<MyObject> { new MyObject { Id = 999, ParentId = 1000 }, new MyObject { Id = 1000 } };
      var myObject = new MyObject { Id = 999 };

      var myMock = new Mock<IDBCLASS>();
      myMock.Setup(o => o.GETBYType(It.IsAny<Expression<Func<MyObject, bool>>>()))
        .Returns((Expression<Func<ShareholderTransaction, bool>> predicate) =>
        {
          return new List<MyObject>().Where(o => o.ParentId == myObject.Id).ToList();
        });
      // ?????????? not sure if this is correct
      var testRecursion = new TestRecursion(myMock.Object);
      testRecursion.Recursive(999, rootList);

    }
  }

  public class TestRecursion
  {
    private IDBCLASS dbClass;

    public TestRecursion(IDBCLASS dbClass)
    {
      this.dbClass = dbClass;
    }

    public void Recursive(int id, List<MyObject> list)
    {
      var parentObject = this.dbClass.GETBYType<MyObject>(x => x.ParentId == id).FirstOrDefault();

      if (parentObject == null)
      {
        return;
      }

      list.Add(parentObject);
      Recursive(parentObject.Id, list);
    }
  }

  public class MyObject
  {
    public int ParentId { get; set; }

    public int Id { get; set; }

  }

  public class DBCLASS : IDBCLASS
  {

    public List<T> GETBYType<T>(Expression<Func<T, bool>> p) where T : class
    {
      // return something from DB;
    }

  }
  public interface IDBCLASS
  {
    List<T> GETBYType<T>(Expression<Func<T, bool>> p) where T : class;
  }

Upvotes: 0

Views: 2058

Answers (1)

Fabio Salvalai
Fabio Salvalai

Reputation: 2509

You have basically two different options:

Either you have a big Arrange section (as in AAA, Arrange Act Assert), or you use a little hack with a minor downside:

Big Arrange approach

as @Brian said in a comment to your question, you could setup GETBYType to return a different value every time it is called.

The approach he proposes could work, however, a simple, cleaner approach exists: You can use the SetupSequence method.

In essemce, the usage should be something like:

var myMock = new Mock<IDBCLASS>();
myMock.SetupSequence(o => o.GETBYType(It.IsAny<Expression<Func<MyObject, bool>>>()))
    .Returns(new MyObject { Id = 999, ParentId = 1000 })
    .Returns(new MyObject { Id = 1000 } )
    .Returns(null);

The Self hack

There is another approach, which doesn't require you to have a complex arrange for the whole recursion. The idea behind it is to only assert that the recursion is continued correctly.

The problem we would be facing here is that since it's a recursive call, we can't make assertions on the subject under test. In order to work around the problem, we can leverage a little hack. it's not exactly clean, but it will allow you to reduce dramatically the number of tests.

The trick is to inject an object of type TestRecursion in your TestRecursion class. At that point, you would then have two constructors: A parameterless constructor, and a constructor accepting an instance of TestRecursion. The constructor accepting a TestRecursion object will keep it as a field (let's call it this.self), in order to call the recursive method on that object.

Now, the constructor accepting a TestRecursion object should NEVER be used by the production code - only by the tests, except in one place: the parameterless constructor will call the constructor expecting a TestRecursion object, passing this as an argument.

Other than this exception, the production code will always call the parameterless constructor. Unfortunately, this parameterless constructor will not be easily testable and this is the big downside of the workaround. However, it will now be easy, from the tests, to inject a mocked TestRecursion and verify that the recursive method was called the correct number of times with the right arguments.

Of course, the implementation of the Recursive method should be modified a little bit, by replacing the following line

  Recursive(parentObject.Id, list);

by

  this.self.Recursive(parentObject.Id, list);

At that point it should also be much easier to test the stop condition.

hope this helps.

Upvotes: 3

Related Questions