wake-up-neo.com

Wie kann ich einen Dienst in symfony2 testen?

Da ich mit Diensten arbeite, wird diese Frage möglicherweise zu einem Problem mit Abhängigkeitsinjektion in Symfony. Momentan versuche ich, eine einfache Funktion in meinem Dienst über phpunit test zu testen, und ich erhalte die folgende Fehlermeldung:

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

Aufgrund des Fehlers ist es offensichtlich, dass ich versuche, eine Instanz meines Dienstes zu erstellen, und das richtige Argument nicht übergeben wird. Daher ist meine services.yml-Datei unten:

#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 ist mein Dienst, den ich gebaut habe:

<?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));
    }
}

Meine Testdatei wird hier angezeigt:

<?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);
    }
}

Zuletzt werden meine Dienste in der Datei app/config.yml registriert:

imports:
    - { resource: parameters.yml }
    - { resource: security.yml }
    - { resource: "@CaremonkMainSiteBundle/Resources/config/services.yml" }
# Other config.yml stuff

Ich bekomme keine so gute Abhängigkeit und ich hoffe, dass meine Interpretation davon, wie in diesem Beitrag gezeigt, den Vorstellungen von Symfony nahe kommt. Bitte lassen Sie mich wissen, was ich falsch mache, damit ich meinen Service testen kann.

35
Dr.Knowitall

In Ihrem Testfall erhalten Sie in Ihrem Konstruktor nichts. Sie können das zu testende Objekt in einer setupBeforeClass- oder Setup-Methode einrichten, z.

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');
}

(Beachten Sie auch, dass Sie Ihre Testklassen auch nicht als Dienste einrichten müssen, sodass Sie Ihren geo_test-Dienst aus services.yml entfernen können.)

Sobald Sie das getan haben, sollten Sie in der Lage sein, Ihren Testfall mit so etwas auszuführen

cd /root/of/project
phpunit -c app src/Caremonk/MainSiteBundle/Tests/Services/GeoTest.php
48
Paul Dixon

Wenn Sie keine Controller (mit Client) testen, sollte Ihre Testklasse KernelTestCase (Symfony\Bundle\FrameworkBundle\Test) erweitern. Um den Kernel zu starten und Dienste zu erhalten, können Sie in Ihrem Test-Setup Folgendes tun:

use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;

class MyTest extends KernelTestCase
{
  public function setUp()
  {
    $kernel = self::bootKernel();
    $this->geo = $kernel->getContainer()->get('geo');
  }
  ...
}

Weitere Informationen: http://symfony.com/doc/current/testing/doctrine.html

Anmerkungen: 

  1. Um neue Kernel und Services für jeden Test zu haben, empfehle ich, die setUp-Methode anstelle von setUpBeforeClass zu verwenden (wie in der akzeptierten Antwort). 
  2. Der Kernel ist immer über static::$kernel (nach dem Booten) erreichbar.
  3. Funktioniert auch in Symfony 3 & 4.
27
Rob

Wenn Sie keine Tests in einer Anwendung, sondern in einem eigenständigen Paket durchführen möchten, schlage ich Ihnen vor, die sehr detaillierte Antwort auf diese Frage zu lesen.

0
Gnucki