Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unit test a function, which just calls other functions?

There is a function in my codebase (legacy), which has:

function test()
{
 method1("#input a")
 method2("test")
 method3(1,2)
}

given the fact it calls other methods, how one will write a good unit testing for these sort of functions?

like image 844
batman Avatar asked Mar 16 '15 08:03

batman


2 Answers

First, I do not think such a behavior as you described even need to have a unit test. BUT, If you really need to check if the methods are called with specific parameters (or even any parameters). There is a way you could do that, you could use a mocking framework like ShortifyPunit: https://github.com/danrevah/ShortifyPunit

Follow this steps:

  1. Make sure the methods attached to an object you can mock
  2. Inject a mocked object to your class and stub the methods
  3. You could verify() the methods were called with specific parameters

For example if your class is using the dependency injection, which is crucial for unit testing like the following class:

class SomeClass {

    private $obj;

    public function __construct($obj) {
        $this->obj = $obj;
    }

    public function test()
    {
        $this->obj->method1("#input a")
        $this->obj->method2("test")
        $this->obj->method3(1,2)
    }
}

You could write a unit test as follows:

public function testMethodCalled()
{
    $mockedObj = ShortifyPunit::mock('object');

    $class = new SomeClass($mockedObj);

    // Stub the methods so you could verify they were called
    ShortifyPunit::when($mockedObj)->method1(anything())->returns(1);
    ShortifyPunit::when($mockedObj)->method2(anything())->returns(2);
    ShortifyPunit::when($mockedObj)->method3(anything(), anything())->returns(3);

    $class->test(); // run test() method

    // Verify it was called
    $this->assertTrue(ShortifyPunit::verify($mockedObj)->method1(anything())->calledTimes(1));
    $this->assertTrue(ShortifyPunit::verify($mockedObj)->method2(anything())->calledTimes(1));
    $this->assertTrue(ShortifyPunit::verify($mockedObj)->method3(anything(), anything())->calledTimes(1));
}
like image 59
D_R Avatar answered Oct 09 '22 01:10

D_R


given the fact it calls other methods (…)

It doesn't matter what it calls. From consumer (caller) point of view the end result is important. You always test the end result. The fact that some other methods are called is irrelevant implementation detail (in most cases).

I suggest simple exercise - inline method1, method2 and method3 bodies into test method. Would you have problems identifying what to test?

You want to test the public contract, or observable behavior - the end result of method call visible for someone calling it (many different actors might call your method - other parts of program, unit test or system user; each should experience the same observable behavior).

Now, back to the "end result"/"method's observable behavior" which was mentioned several times. It can be one of three things:

  • returned value (your method doesn't do that)
  • call to third party components (your method might be doing that)
  • change in system's internal state (your method seem to be doing that)

You need to identify the end result and test against it.

like image 38
k.m Avatar answered Oct 08 '22 23:10

k.m