I would like to test some of my services, but I can not find any example on Laravel's website: https://laravel.com/docs/5.1/testing
They show how to test simple classes, entities, controllers, but I have no idea how to test services. How is it possible to instantiate a service with complex dependencies?
Example service:
<?php
namespace App\Services;
// Dependencies
use App\Services\FooService;
use App\Services\BarService;
class DemoService {
private $foo_srv;
private $bar_srv;
function __construct(
FooService $foo_srv,
BarService $bar_srv
) {
$this->foo_srv = $foo_srv;
$this->bar_srv = $bar_srv;
}
// I would like to test these two functions
public function demoFunctionOne() {
// ...
}
public function demoFunctionTwo() {
// ...
}
}
PHPUnit is a unit testing framework for the PHP programming language. It is an instance of the xUnit design for unit testing systems that began with SUnit and became popular with JUnit. Even a small software development project usually takes hours of hard work.
Use Laravel Unit Testing to Avoid Project-Wrecking Mistakes. Pardeep Kumar. 7 Min Read. PHPUnit is one of the most well known and highly optimized unit testing packages of PHP. It is a top choice of many developers for rectifying different developmental loopholes of the application.
PHPUnit is a framework independent library for unit testing PHP. Unit testing is a method by which small units of code are tested against expected results. Traditional testing tests an app as a whole meaning that individual components rarely get tested alone.
The quickest thought that might come to the mind is to create a copy of those Service classes, but that could grow so big. This is why there's MockObject in PhpUnit.
To achieve this mock and use it as a replacement to the service class you may need to resolve it in Laravel service container.
Here is how it will look:
class DemoServiceTest extends TestCase
{
// Dependencies
use App\Services\FooService;
use App\Services\BarService;
use App\Services\DemoService;
public function testDemoFunctionOne()
{
$foo_srv = $this->getMockBuilder(FooService::class)
//->setMethods(['...']) // array of methods to set initially or they return null
->disableOriginalConstructor() //disable __construct
->getMock();
/**
* Set methods and value to return
**/
// $foo_srv->expects($this->any())
// ->method('myMethod') //method needed by DemoService?
// ->will($this->returnValue('some value')); // return value expected
$bar_srv = $this->getMockBuilder(BarService::class)
// ->setMethods(['...']) // array of methods to set initially or they return null
->disableOriginalConstructor()
->getMock();
/**
* Set methods and value to return
**/
// $bar_srv->expects($this->any())
// ->method('myMethod') //method needed by DemoService?
// ->will($this->returnValue('some value')); // return value expected
$demo_service = new DemoService($foo_srv, $bar_srv);
$result = $demo_service->demoFunctionOne(); //run demo function
$this->assertNotEmpty($result); //an assertion
}
}
We are creating new mock for both FooService
and BarService
classes, and then passing it when instantiating DemoService
.
As you can see without uncommenting the commented chunk of code, then we set a return value, otherwise when setMethods
is not used, all methods are default to return null.
Let's say you want to resolve these classes in Laravel Service Container for example then you can after creating the mocks, call:
$this->app->instance(FooService::class, $foo_srv);
$this->app->instance(BarService::class, $bar_srv);
Laravel resolves both classes, feeding the mock classes to any caller of the classes So that you just call your class this way:
$demoService = $this->app->make(DemoService::class);
There are lots of things to watch out for when mocking classes for tests, see sources: https://matthiasnoback.nl/2014/07/test-doubles/, https://phpunit.de/manual/6.5/en/test-doubles.html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With