I have to write a test class to test my service methods. In my controllers I could access the service by doing $service = $this->get('myService'); and I could access my methods by doing $service->someMethod();.
Now I want to write a test class to test some of the service methods, I tried doing like the doc :
class ServiceTest extends \PHPUnit_Framework_TestCase {
public function testSomeMethod() {
$service = $this->get('myService');
....
}
}
When I launch the test I get something like : Fatal error : Call to undefined method XXX\XXXBundle\Tests\Services\ServicesTest::get() in ...
So my question is how can I use get method to be able to call my service methods
You don't need to use the container in order to test services and other classes.
You should create a new instance of the class and inject the dependencies using test doubles.
Test doubles
- since you don't want to write a functional testing and to use the real dependencies you should be using a test double.
for example if one of the service dependencies is the EntityManager what would happen is that a new entries would be inserted into the Database (or deleted from it) and its not the purpose of Unit testing (you do need to check this tho if you are writing a functional test).
And that's how you should approach this kind of testings:
public function testSomeServiceAdd()
{
$dependencyOne = $this->getMockBuilder('Acme\SomeBundle\DependencyOne')->
disableOriginalConstructor()->
getMock();
$dependencyTwo = $this->getMockBuilder('Acme\SomeBundle\DependencyTwo')->
disableOriginalConstructor()->
getMock();
$service = new SomeService($dependencyOne, $dependencyTwo);
$response = $service->add(1, 2);
$this->assertEquals($response, 3);
}
As you can see I am "mocking" the dependencies and injecting it into the service, after that i'm calling the method and asserting the result.
I would also like to suggest a Mocking framework instead of using the built-in PHPUnit mocking functions. it's easier to use and it has much more mocking functionality:
https://github.com/danrevah/ShortifyPunit
To test your services you should extend the KernelTestCase class.
class ServiceTest extends KernelTestCase
{
private $service;
/**
* {#inheritDoc}
*/
public function setUp()
{
self::bootKernel();
$this->service = static::$kernel->getContainer()
->get('my_service')
;
}
}
Related
How can I mock/stub a method on a model correctly (in laravel)? Currently I am trying...
$mock = Mockery::spy(Organisation::class);
$mock->shouldReceive('findByCustomerId')->once();
and the code under test is
use App\Organisation;
...
public function handle()
{
$org = Organisation::findByCustomerId(1234);
However when I run the tests I get an error Call to undefined method App\Organisation::findByCustomerId(), which tells me that the class/model is not being mocked correctly, does anyone know where I could be going wrong?
Mockery doesn't automatically overload classes when you create a mock for them (like some other libraries might do). It does support overloading, but I couldn't figure out how to overload a class with a partial mock.
So it looks like you'll have to use dependency injection to "inject" the mock object into the class that is being tested:
class TestedClass
{
private $organisation;
public function __construct(Organisation $organisation)
{
$this->organisation = $organisation;
}
public function handle()
{
$org = $this->organisation->findByCustomerId(1234);
}
}
Laravel with automatically inject the correct object for you. To inject your mock object instead of the Organization class you can do something like this in your test:
$mock = \Mockery::spy(Organization::class)->makePartial();
$mock->shouldReceive('findByCustomerId')->once();
$this->app->instance(Organization::class, $mock);
I have a client class which uses the magic __call method to construct and endpoint class.
class Client
{
public function __call($name, $arguments)
{
$className = "\\App\\Endpoints\\" . ucfirst($name);
if (! class_exists($className)) {
throw new InvalidEndpointException("The `{$name}` endpoint was not configured.");
}
return new $className($this->getHttpClient());
}
// other methods
}
My namespace for endpoints is App\Endpoints. Worth mentioning that all my endpoints extend from an abstract Endpoint class which contains all the logic needed. The endpoints themselves are pretty much empty with them only containing the endpoint url or in some cases some additional methods non REST methods.
Now I want to test the magic method and don't really want to use a real endpoint from that namespace but rather want to write my own test double. In order to do that I have to put the test double inside the ClientTest class under the real namespace App\Endpoints. This class will only be available for this test.
This is what I have in my ClientTest class
namespace App\Tests;
class ClientTest extends TestCase
{
public function testItReturnsTheCorrectEndpointWhenCalled()
{
$resources = $this->client->myTestResources();
$this->assertInstanceOf(Endpoint::class, $resources);
}
}
namespace App\Endpoints;
class MyTestResources extends Endpoint
{
public static $endpoint = 'myTestResources';
}
Is there a better way to achieve this?
A mock is probably the best way to do this. There’s some notes about them in the PHPUnit docs, or you could refer to your framework (if you’re using one). Laravel, for example, has a range of mocks.
I'm still in the process of learning about Laravel and Dependency Injection. I understand the concept, but I don't know how to mock a dependency in this specific case:
MyController.php
use Illuminate\Routing\Controller;
use MyPackage\Services\ServiceInterface;
class MyController extends Controller{
protected $service;
public function __construct(ServiceInterface $service)
{
$this->service = $service;
}
}
MyServiceProvider.php
use Illuminate\Support\ServiceProvider;
class MyServiceProvider extends ServiceProvider{
public function register()
{
$this->app->bind('MyPackage\Services\ServiceInterface', function ($app) {
return new MyPackage\Services\ConcreteService(['key'=>'123'], $app->make('GuzzleHttp\Client'));
});
}
}
So, as you can see, I have a controller that requires an instance of ServiceInterface. That instance is being resolved in the ServiceProvider. The constructor of ConcreteService requires a client to perform Http request to an API. This Http is being resolved by the Service container (It will be an instance of Guzzle).
Now, how can I mock this instance of Guzzle on my tests?
The ideal result is doing something like this:
MyTest.php
...
$this->post(route('routeToActionInMyController'), $params);
So, in my tests I just need to hit the route that will be using an specific method of MyController.php but I don't need a "real" Guzzle instance. I just need to mock the response to test if MyController behaves in the expected way (and stores things in the database properly).
How can I instruct the Service Container to inject a Mocked object during tests only? Or am I doing this in the completely wrong way?
Any help will be appreciated.
Thanks in advance
In your test class:
class TestingSomething extends TestCase {
protected function setUp() {
parent::setUp();
$mockServiceInterface = $this->getMockBuilder(ServiceInterface::class)->getMock();
$this->app->instance(ServiceInterface::class,$mockServiceInterface);
}
public function testPostToRoute() {
$this->post(route('routeToActionInMyController'), $params);
}
}
This should replace what's already bound in the service container with that mock instance.
Refer to the PHPUnit manual on chapter 9. Test doubles for what you can do with the mock builder and resulting mocks.
I am new to Symfony, I have wrote small app now have to add unit tests, here is my controller:
<?php
namespace myBundle\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
class IndexController extends AbstractController
{
/**
* #param \Symfony\Component\HttpFoundation\Request $request
* #return \Symfony\Component\HttpFoundation\Response
*/
public function indexAction(Request $request)
{
if ($this->getRequest()->isMethod('POST')) {
// Do Something
}
// Do Something otherwise
}
}
My test:
class IndexControllerTest extends \PHPUnit_Framework_TestCase
{
protected $testController;
public function setUp()
{
$this->testController =
$this->getMockBuilder('myBundle\Controller\IndexController')
->disableOriginalConstructor()
->getMock();
}
public function testPostSaveActionWithBadRequest()
{
$expectation = 'Some text ';
$response = $this->testController->indexAction(new Request);
$this->assertInstanceOf(
'Symfony\Component\HttpFoundation\JsonResponse',
$response
);
$content = json_decode($response->getContent());
$this->assertEquals($expectation, $content->error);
}
}
When I run this test I get following:
PHP Fatal error: Call to a member function get()
which is basically on following line
if ($this->getRequest()->isMethod('POST')) {
this tells me the container is null (I verified it by printing dump of the container).
any idea what am I missing here or is there a way to provide container as dependency for that test.
I really appreciate all the help.
thanks
FI
You're trying to mock the class you're suppose to test:
$this->testController =
$this->getMockBuilder('myBundle\Controller\IndexController')
->disableOriginalConstructor()
->getMock();
You should actually instantiate the class you're testing, and mock or stub its collaborators.
However, in this particular scenario, instead of writing a unit test, write a functional test. There's a chapter on writing functional tests in Symfony docs that'll help you.
Your controller uses lots of framework classes (classes that don't belong to you), and you shouldn't mock them either. That's why functional tests are better in this case. Also, make sure you move as much code as possible out of your controller, so you can properly unit test that part (and write as little functional tests as possible).
In the meantime read some books on unit testing (in the following order):
TDD by Example
Growing Object-Oriented Software Guided by Tests
For objects which compose another object as part of their implementation, what's the best way to write the unit test so only the principle object gets tested? Trivial example:
class myObj {
public function doSomethingWhichIsLogged()
{
// ...
$logger = new logger('/tmp/log.txt');
$logger->info('some message');
// ...
}
}
I know that the object could be designed so that the logger object dependency could be injected and hence mocked in a unit test, but that's not always the case - in more complicated scenarios, you do need to compose other objects or make calls to static methods.
As we don't want to test the logger object, only the myObj, how do we proceed? Do we create a stubbed "double" with the test script? Something like:
class logger
{
public function __construct($filepath) {}
public function info($message) {}
}
class TestMyObj extends PHPUnit_Framework_TestCase
{
// ...
}
This seems feasible for small objects but would be a pain for more complicated APIs where the SUT depended on the return values. Also, what if you want to test the calls to the dependency object in the same was you can with mock objects? Is there a way of mocking objects which are instantiated by the SUT rather than being passed in?
I've read the man page on mocks but it doesn't seem to cover this situation where the dependency is composed rather than aggregated. How do you do it?
Following troelskn advise here's a basic example of what you should do.
<?php
class MyObj
{
/**
* #var LoggerInterface
*/
protected $_logger;
public function doSomethingWhichIsLogged()
{
// ...
$this->getLogger()->info('some message');
// ...
}
public function setLogger(LoggerInterface $logger)
{
$this->_logger = $logger;
}
public function getLogger()
{
return $this->_logger;
}
}
class MyObjText extends PHPUnit_Framework_TestCase
{
/**
* #var MyObj
*/
protected $_myObj;
public function setUp()
{
$this->_myObj = new MyObj;
}
public function testDoSomethingWhichIsLogged()
{
$mockedMethods = array('info');
$mock = $this->getMock('LoggerInterface', $mockedMethods);
$mock->expects($this->any())
->method('info')
->will($this->returnValue(null));
$this->_myObj->setLogger($mock);
// do your testing
}
}
More information about mock objects can be found in the manual.
As you seem to be aware already, Concrete Class Dependencies makes testing hard (or outright impossible). You need to decouple that dependency. A simple change, that doesn't break the existing API, is to default to the current behaviour, but provide a hook to override it. There are a number of ways that this could be implemented.
Some languages have tools that can inject mock classes into code, but I don't know of anything like this for PHP. In most cases, you would probably be better off refactoring your code anyway.
Looks like I misunderstood the question, let me try again:
You should use the singleton pattern or a factory for the logger, if it's not too late already:
class LoggerStub extends Logger {
public function info() {}
}
Logger::setInstance(new LoggerStub());
...
$logger = Logger::getInstance();
If you can't change the code, you could use a catch-all class that is overloading __call()
class GenericStub {
public function __call($functionName, $arguments) {}
}
There is actually a reasonably new extension for PHP class overloading released by the same guys that build PHPUnit. It lets you override the new operator in cases where you can't refactor the code, unfortunately it isn't that simple to install on Windows.
The URL is http://github.com/johannes/php-test-helpers/blob/master/