How can I test a service in symfony2?
Solution 1
In your test case, you're not going to get anything in your constructor - you can set up the object you want to test in a setupBeforeClass or setup method, e.g.
public static function setUpBeforeClass()
{
//start the symfony kernel
$kernel = static::createKernel();
$kernel->boot();
//get the DI container
self::$container = $kernel->getContainer();
//now we can instantiate our service (if you want a fresh one for
//each test method, do this in setUp() instead
self::$geo = self::$container->get('geo');
}
(Note also you don't need to set up your test classes as services either, so you can remove your geo_test service from services.yml)
Once you've done that, you should be able to run your test case with something like
cd /root/of/project
phpunit -c app src/Caremonk/MainSiteBundle/Tests/Services/GeoTest.php
Solution 2
If your are not going to test controllers (with client, because controller called like this will use a new container) your test class should just extend KernelTestCase (Symfony\Bundle\FrameworkBundle\Test). To boot the kernel & get services you can do sth like this in your test setup:
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
class MyTest extends KernelTestCase
{
public function setUp()
{
$kernel = self::bootKernel();
$this->geo = $kernel->getContainer()->get('geo');
}
...
}
More info: http://symfony.com/doc/current/testing/doctrine.html
Notes:
- To have fresh kernel & services for each test I recommend to use the
setUp
method instead ofsetUpBeforeClass
(like in the accepted answer). - The kernel is always accessible via
static::$kernel
(after booting). - Also works in symfony 3 & 4.
Dr.Knowitall
I'm a fullstack engineering breaking into the data science world. I'm interested in computer visualization, linguistics processing, machine learning and their applications in quantitative finance.
Updated on July 10, 2022Comments
-
Dr.Knowitall almost 2 years
Since I'm working with services, this question may end up being an issue with dependency-injection in symfony. Currently I'm trying to test one simple feature in my service via phpunit test and I keep getting the following error:
PHP Catchable fatal error: Argument 1 passed to Caremonk\MainSiteBundle\Tests\Services\GeoTest::__construct() must be an instance of Caremonk\MainSiteBundle\Tests\Services\Geo, none given, called in /usr/share/nginx/html/caremonk/vendor/phpunit/phpunit/PHPUnit/Framework/TestSuite.php on line 473 and defined in /usr/share/nginx/html/caremonk/src/Caremonk/MainSiteBundle/Tests/Services/GeoTest.php on line 14
From the error, it is obvious that I am trying create an instance of my service and the correct argument is not being passed, so bellow is my services.yml file:
#src/Caremonk/MainSiteBundle/Resources/config/services.yml parameters: caremonk_main_site.geo.class: Caremonk\MainSiteBundle\Services\Geo caremonk_main_site.geo_test.class: Caremonk\MainSiteBundle\Tests\Services\GeoTest services: geo: class: %caremonk_main_site.geo.class% arguments: [] geo_test: class: %caremonk_main_site.geo_test.class% arguments: ["@geo"]
Bellow is my service that I've built:
<?php //src/Caremonk/MainSiteBundle/Services/Geo.php namespace Caremonk\MainSiteBundle\Services; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Request; class Geo extends Controller { public $pi80; public $latRad; public $lngRad; public function __construct() { $this->pi80 = M_PI / 180; } // Takes longitude and latitude and converts them into their respective radians // We also set our class properties to these values public function setCoordinates($lat,$lng) { $this->latRad = $lat * $this->pi80; $this->lngRad = $lng * $this->pi80; } public function distance($lat2, $lng2, $miles = true) { $lat1 = $this->latRad; $lng1 = $this->lngRad; $lat2 *= $pi80; $lng2 *= $pi80; $r = 6372.797; // mean radius of Earth in km $dlat = ($lat2 - $lat1)/2; $dlng = ($lng2 - $lng1)/2; $a = sin($dlat) * sin($dlat) + cos($lat1) * cos($lat2) * sin($dlng) * sin($dlng); $c = 2 * atan2(sqrt($a), sqrt(1 - $a)); $km = $r * $c; return ($miles ? ($km * 0.621371192) : $km); } // This function returns the minimum latitude in radians public function min_lat($lat,$lng,$dis) { $dis /= .62137119; $ratio = $dis/6372.797; return asin(sin($lat)*cos($ratio) + cos($lat)*sin($ratio)*cos(M_PI)); } // This function returns the max latitude in radians public function max_lat($lat,$lng,$dis) { $dis /= .62137119; $ratio = $dis/6372.797; return asin(sin($lat)*cos($ratio) + cos($lat)*sin($ratio)*cos(0)); } // This function returns max longitude in radians public function max_lon($lat,$lng,$dis) { $dis /= .62137119; $ratio = $dis/6372.797; return $lng + atan2(sin(M_PI/2)*sin($ratio)*cos($lat),cos($ratio)-sin($lat)*sin($lat)); } // This function returns min longitude in radians public function min_lon($lat,$lng,$dis) { $dis /= .62137119; $ratio = $dis/6372.797; return $lng + atan2(sin(M_PI*1.5)*sin($ratio)*cos($lat),cos($ratio)-sin($lat)*sin($lat)); } }
My test file is shown here:
<?php //src/Caremonk/MainSiteBundle/Tests/Services/GeoTest.php namespace Caremonk\MainSiteBundle\Tests\Services; use Caremonk\MainSiteBundle\Services; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Component\DependencyInjection\ContainerBuilder; class GeoTest extends WebTestCase { public $geo; public function __construct(Geo $geo) { $this->geo = $geo; } public function testSetCoordinates() { $this->geo->setCoordinates(4,5); //print $geoService->distance(6,5); } }
Lastly, my services are registered bellow in the app/config.yml file:
imports: - { resource: parameters.yml } - { resource: security.yml } - { resource: "@CaremonkMainSiteBundle/Resources/config/services.yml" } # Other config.yml stuff
I don't get dependency that well and I'm hoping that my interpretation of it as shown in this post is close to what symfony had in mind. Please let me know what I'm doing wrong so I can test my service.
-
geonunez over 9 yearsPerfect solution, I only add that your test class has to extends from WebTestCase if you are working with symfony 2.4.x
-
ZvL over 9 yearsSaved me a lot of time! Thanks.
-
Anand G over 8 yearsExcellent answer! Been looking for this since last two days
-
Remy Mellet over 8 yearsworks fine with Symfony 2.6 adding "extends WebTestCase" to the tested class. (use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;) I used that to test the construction of translated sentence