5

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.

4
  • 2
    You can't. That's why people are so obsessed about autowiring and dependency injection: if your time provider or your database handler were non-hardwired, you could easily mock it for testing without changing the source. As it is, you have to essentially wait until Monday to run the test, which is a Really Bad Thing. Mar 18, 2017 at 21:26
  • Your comment seems to be the answer. I have read a little about dependency injection, but it seems to be used for various things and I am having trouble relating the specifics to what I am trying to do (unit-testing). I could not find much on autowiring (only related to Spring). Can you post an answer explaining these two concepts with some good examples of how they can be used to unit-test... or direct me to a SO question that does. My terminology is weak, so searches are coming up blank.
    – user58446
    Mar 18, 2017 at 21:50
  • This question has some information but indicates it is not necessary for unit-testing. Not sure how this could be in the example I gave.
    – user58446
    Mar 18, 2017 at 21:52
  • Depends on the language I guess. In c#, it is certainly possible using fakes. See this answer for an example of swapping out DateTime.Now with your own shim.
    – John Wu
    Mar 18, 2017 at 23:49

3 Answers 3

5

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.

0

First, I am convinced it is not a very effective approach trying to test a function which was not written in a testable manner in conjunction with some "no change to the source code" policy. Often, some changes to the source code take only a small effort and have a very low risk of breaking anything, whilst reducing the effort of writing unit tests by an order of magnitude.

However, depending on the actual programming language, it may be possible to embed these functions into a testing environment where the variables system or database can be initialized by "test objects" or "mocks" from outside. That can make it possible to test the functions of your example without changing anything in source code file where the functions you want to test "lives".

For example, in C or C++, you might be able to utilize the preprocessor for this, or include files which are different in the test environment from the development enviroment. In Java or C#, you may be able to set up a special test project, containing a mock "database" and a mock "system" object for this, and reference the "function under test" into this environment.

However, this is much easier if you change the code slightly in a way you can inject the "system" or the function system.day directly by the caller. Michael Feathers called this creating a seam in your code - a place where you can alter behavior without editing the code in place.

0

I'm a PHP developer, so I'll tell you how it's done in this language. But I'm sure most languages have something similar. It's a sufficiently common problem that I'm sure most testing-frameworks have figured out at least an acceptable way of solving it. You just have to search for it for your particular language/platform.

The best example I can give in PHP is the ClockMock implemented in the PHPUnit bridge (part of the Symfony Framework): https://github.com/symfony/phpunit-bridge/blob/master/ClockMock.php.

The language has some native time-related functions, like for example sleep().

Let's say you have this in your code:

class A
{
    public function pauseSystemForAWhile()
    {
        sleep(2);
    }
}

When it runs in production, of course the code will sleep for ~2000ms.

However, in the case of unit-tests, the class I mentioned above "hooks" itself into the language and overrides the definition of that function. If you look at how it's implemented, you see that it simply uses an internal counter which gets incremented during unit-test execution.

To effectively use this, I simply mark my unit-test using a specific group:

/**
 * @group time-sensitive
 */
class ATest extends \PHPUnit_Framework_TestCase
{
    public function testPauseSystemForAWhile()
    {
        $now = time();

        $a = new A();
        $a->pauseSystemForAWhile();

        $afterPause = time();

        //check that 2 seconds have passed.
        $this->assertEquals(
            2,
            $afterPause - $now
        );
    }
}

As you can see, implementation underneath the hood is not so pretty and I'm sure it took a while for the framework's developers to get it right. But it's really elegant and easy to use.

Unfortunately I'm not aware of how "mocking" native language functions can be done in other languages but, like I said in the beginning, one surely exists for your language.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.