Reputation: 3685
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?
Upvotes: 8
Views: 12256
Reputation: 10982
This is very good question and I too struggled with it for a long time. In your case you have a function test
which you want to test , but the function has dependencies — method1-3
.
There are basically two ways —
test
function.Example —
function test(method1, method2, method3) {
method1("#input a")
method2("test")
method3(1,2)
}
The second approach would give you the flexibility to mock the methods 1, 2 & 3.
It could also be a mix of case one and two, where some methods are directly used from the global scope and some are passed as params.
The only problem now is to understand when to use what. Here are some tips —
For case one
test
function.For all other cases, you would want to pass the dependencies as params. For example — in this case, it looks like method1
and method2
are doing some DOM operations, which means they should be injected and mocked where as method3
looks pretty simple and can be used from the global scope.
function (d) {
d.method1("#input a")
d.method2("test")
method3(1,2)
}
If the dependencies become too many, you can pass an object containing the all dependencies.
Upvotes: 0
Reputation: 31444
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:
You need to identify the end result and test against it.
Upvotes: 4
Reputation: 4962
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:
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));
}
Upvotes: 4