Unit Testing – Testing Local/Nested Conditional Logic Branches

test-coveragetestingunit testing

It is easy enough to create unit tests for conditional blocks which follow the expected path, but it can sometimes be difficult to contrive data for sources/objects I do not directly control, (databases I do not want to modify or access, environmental variables, etc.) without modifying the source code to add debugging logic (using only unit tests as functions. How would one construct unit tests to test the following specified control blocks?

function(int x)
{
   if(x > 10)
   {
       if(system.day() == "Monday")
           print "Monday"
       else
           // TEST THIS SPACE (BUT ON A MONDAY :)
           print "Not Monday"
   }
   else
   {
       ....
   }
}

In the previous function, I can write a function and pass whatever value I like in for x, but how can I test the nested conditional which relies on a a System.date() call that I cannot (easily) modify?

Another example using databases which I do not control and cannot access:

function(int x)
{
    try
    {
        if(x > 10)
        {
             query_result = database.query()
             if(query_results != NULL)
             {
                 print "QUERY NOT NULL"
             }
             else
             {
                 // TEST THIS SPACE 
                 print "QUERY IS NULL"
             }
        }
    }
}

Obviously, if I was passing in the query, I could control it, but in this case I cannot. These are simple and contrived examples, please expand on these particular cases and any related scenarios which I may not have considered.

Best Answer

You want to use dependency injection. I'll give you an example in C#, since I'm most familiar with it. Your first example:

class YourClass
{
    ITimeService _timeService;
    IOutputService _outputService;

    public YourClass(ITimeService timeService, IOutputService outputService)
    {
        _timeService = timeService;
        _outputService = outputService;
    }    

    public function(int x)
    {
       if(x > 10)
       {
           if(_timeService.GetDayOfWeek() == "Monday")
               _outputService.Print("Monday")
           else
               // TEST THIS SPACE (BUT ON A MONDAY :)
               _outputService.Print("Not Monday")
       }
       else
       {
           ....
       }
    }

where the two services are defined as following

interface ITimeService
{
    string GetDayOfWeek();
}

interface IOutputService 
{
    void Print(string text);
}

and implemented like this:

class RealTimeService : ITimeService
{
    public string GetDayOfWeek()
    {
        return system.day(); //not C#
    }
}

class RealOutputService : IOutputService
{
    public void Print(string text)
    {
        print text; //not C#
    }
}

Now in your program, in your main function (also known as 'composition root' using dependency injection terminology), you would instantiate your class as following:

void main()
{
     YourClass yourClass = new YourClass(new RealTimeService(), new RealOutputService());

     yourClass.function(11);
}

However, if you wanted to test your code, instead of injecting RealTimeService and RealOutputService, you would inject FakeTimeService, and FakeOutputService. For example, this FakeTimeService will let you return any day of the week you want:

public class FakeTimeService : ITimeService
{
    private string _dayOfWeek;

    public FakeTimeService(string dayOfWeek)
    {
        _dayOfWeek = dayOfWeek;
    }

    public string GetDayOfWeek()
    {
        return _dayOfWeek;
    }
}

and this FakeOutputService will store whatever was output to it, so your test can verify it was correct:

public class FakeOutputService : IOutputService
{
    public string _printedText = "";

    public Print(string text)
    {
        _printedText += text;
    }
}

Finally, inside your test code you'd instantiate and test the class as follows:

[Test]
void When_It_Is_Monday_Output_Should_Be_Monday()
{
    FakeOutputService fakeOutputService = new FakeOutputService();
    YourClass yourClass = new YourClass(new FakeTimeService("MONDAY"), fakeOutputService);

    yourClass.function(11);

    Assert.AreEqual(fakeOutputService._printedText, "Monday");
}

[Test]
void When_It_Is_Tuesday_Output_Should_Be_Not_Monday()
{
    FakeOutputService fakeOutputService = new FakeOutputService();
    YourClass yourClass = new YourClass(new FakeTimeService("TUESDAY"), fakeOutputService);

    yourClass.function(11);

    Assert.AreEqual(fakeOutputService._printedText, "Not Monday");
}

Now granted, this seems like a lot of typing, which is why most of the time you'd use a mocking framework to do it for you. For example in C#, I would use Moq framework, which takes care of creating fake objects for you.

Also, if you want another DI in Unit Testing example, check out my answer from a few months ago here.