Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you unit test mutually-recursive methods?

I have three functions that looks something like this:

private Node GetNode(Node parentNode)
{
    var node = new node();

    switch (parentNode.NodeType)
    {
       case NodeType.Multiple:    node = GetMultipleNode(parentNode)
       case NodeType.Repeating:   node = GetRepeatingNode(parentNode)
    }

    return node;
}

private Node GetMultipleNode(Node parentNode)
{
    foreach (var child in parentNode.Children)
        return GetNode(child);
}

private Node GetRepeatingNode(Node parentNode)
{
    for (int i=0; i < parentNode.Count; i++)
         return GetNode(new Node(i));  // Assume meaningful constructor for Node
}

Given that these three methods are mutually recursive, how does one go about unit testing them independently?

like image 851
Robert Harvey Avatar asked Jan 19 '11 21:01

Robert Harvey


People also ask

How does mutual recursion work?

Mutual recursion is a variation recursion. Two functions are called mutually recursive if the first function makes a recursive call to the second function and the second function, in turn, calls the first one.

What is a recursive test?

A recursive function typically contains a conditional expression which has three parts: A true-or-false-test that determines whether the function is called again, here called the do-again-test.

Where are we most likely to find mutual recursion?

Mutual recursion is very common in functional programming, and is often used for programs written in LISP, Scheme, ML, and similar programming languages.


2 Answers

Normally you wouldn't need to test each method individually - you can just test that the top-level method does the right thing.

However if for some reason you want to test each method separately you can use dependency injection just as you would test any method that has dependencies. The only difference here is that the dependency is the object itself. Here is some example code to demonstrate the idea:

class NodeGetter : INodeGetter
{
    public Node GetNode(Node parentNode)
    {
        return GetNode(parentNode, this);
    } 

    public Node GetNode(Node parentNode, INodeGetter nodeGetter)
    {
        switch (parentNode.NodeType)
        {
           case NodeType.Multiple:
               return nodeGetter.GetMultipleNode(parentNode, nodeGetter);
           case NodeType.Repeating:
               return nodeGetter.GetRepeatingNode(parentNode, nodeGetter);
           default:
               throw new NotSupportedException(
                   "Node type not supported: " + parentNode.NodeType);
        }
    }

    public Node GetMultipleNode(Node parentNode, INodeGetter nodeGetter)
    {
        foreach (Node child in parentNode.Children)
        {
            return nodeGetter.GetNode(child);
        }
    }

    public Node GetRepeatingNode(Node parentNode, INodeGetter nodeGetter)
    {
        for (int i = 0; i < parentNode.Count; i++)
        {
            // Assume meaningful constructor for Node
            return nodeGetter.GetNode(new Node(i));
        }
    }
}

When testing for the nodegetter argument pass a mock.

I also changed your methods from private to public because it is better to only test the public interface of your class.

like image 95
Mark Byers Avatar answered Nov 09 '22 02:11

Mark Byers


Well, you can't unit test them "independently" since they obviously depend on one another, but in principle you can certainly write separate tests for GetNode, GetMultipleNode, and GetRepeatingNode, assuming it makes sense to call each of these from the code that uses them. Sure, GetRepeatingNode calls GetNode, and so on, but that's no different from it calling some totally external function.

Incidentally, you might consider refactoring your design and use polymorphism instead of your NodeType enumeration. Just an idea :)

like image 28
500 - Internal Server Error Avatar answered Nov 09 '22 02:11

500 - Internal Server Error