If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeWe'll have a bunch of test classes and they'll all need to create a Guzzle Client
with these options. So let's just get organized now.
Create a new Test
directory in the bundle and a new class called ApiTestCase
. This will be a base class for all our API tests. Make it extend the normal PHPUnit_Framework_TestCase
:
... lines 1 - 2 | |
namespace AppBundle\Test; | |
... lines 4 - 6 | |
class ApiTestCase extends \PHPUnit_Framework_TestCase | |
{ | |
... lines 9 - 29 | |
} |
Right now, the thing I want to move out of each test class is the creation of the Guzzle Client
. So copy that code. In ApiTestCase
, override a method called setupBeforeClass()
- it's static. PHPUnit calls this one time at the beginning of running your whole test suite.
Paste the $client
code here. Because really, even if we run A LOT of tests, we can probably always use the same Guzzle client. Create a private static
property called $staticClient
and put the Client
there with self::$staticClient
. And give Client
a proper use
statement:
... lines 1 - 6 | |
class ApiTestCase extends \PHPUnit_Framework_TestCase | |
{ | |
private static $staticClient; | |
... lines 10 - 15 | |
public static function setUpBeforeClass() | |
{ | |
self::$staticClient = new Client([ | |
'base_url' => 'http://localhost:8000', | |
'defaults' => [ | |
'exceptions' => false | |
] | |
]); | |
} | |
... lines 25 - 29 | |
} |
Tip
In case you are using Guzzle 6, you would need to use the base_uri
key instead of base_url
to configure Guzzle client properly.
Cool. So now the Client
is created once per test suite. Now, create a protected $client
property that is not static with some nice PHPDoc above it. Woops - make sure you actually make this protected
: this is what we'll use in the sub-classes. Then, override setup()
and say $this->client = self::$staticClient
:
... lines 1 - 6 | |
class ApiTestCase extends \PHPUnit_Framework_TestCase | |
{ | |
... lines 9 - 10 | |
/** | |
* @var Client | |
*/ | |
protected $client; | |
... lines 15 - 25 | |
protected function setUp() | |
{ | |
$this->client = self::$staticClient; | |
} | |
} |
setupBeforeClass()
will make sure the Client
is created just once and setup()
puts that onto a non-static property, just because I like non-static things a bit better. Oh, and if we did need to do any clean up resetting of the Client, we could do that in setup()
or tearDown()
.
Back in the actual test class, get rid of the $client
code and simply reference $this->client
. Ooooo, and don't forget to extend ApiTestCase
like I just did:
... lines 1 - 3 | |
use AppBundle\Test\ApiTestCase; | |
class ProgrammerControllerTest extends ApiTestCase | |
{ | |
public function testPOST() | |
{ | |
... lines 10 - 16 | |
// 1) Create a programmer resource | |
$response = $this->client->post('/api/programmers', [ | |
'body' => json_encode($data) | |
]); | |
... lines 21 - 25 | |
} | |
} |
Make sure we didn't break anything:
php bin/phpunit -c app src/AppBundle/Tests/Controller/Api/ProgrammerControllerTest.php
Hey, still green!
Hey azeem
Probably you forgot to override the database url parameter in your "phpunit.xml.dist" file
You can find more info here: https://symfony.com/doc/cur...
Cheers!
I have that set already. And, it hits the correct `app_test` schema if I make any changes via entity manager. However, I am testing an api endpoint for e.g. `/app_test.php/tokens` via GuzzleHttp. if I do `var_dump(getenv('APP_ENV'), getenv('database_url'))` I get 'dev' and `mysql://username:secret@localhost:3306/apps`
Hey azeem!
Hmmm. So basically, when you interact with the database inside your test (e.g. add/delete rows), this correctly happens against the test database. But when you actually make the Guzzle request to /app_test.php/tokens, this hits the "dev" environment. Is that correct?
In that case, what does your app_test.php file look like? And also, in what file are you overriding the database credentials for the test environment?
Indeed, the way that you override some of these variables is a bit different in Symfony 4 :).
Cheers!
// composer.json
{
"require": {
"php": ">=5.3.3",
"symfony/symfony": "2.6.*", // v2.6.11
"doctrine/orm": "~2.2,>=2.2.3,<2.5", // v2.4.7
"doctrine/dbal": "<2.5", // v2.4.4
"doctrine/doctrine-bundle": "~1.2", // v1.4.0
"twig/extensions": "~1.0", // v1.2.0
"symfony/assetic-bundle": "~2.3", // v2.6.1
"symfony/swiftmailer-bundle": "~2.3", // v2.3.8
"symfony/monolog-bundle": "~2.4", // v2.7.1
"sensio/distribution-bundle": "~3.0,>=3.0.12", // v3.0.21
"sensio/framework-extra-bundle": "~3.0,>=3.0.2", // v3.0.7
"incenteev/composer-parameter-handler": "~2.0", // v2.1.0
"hautelook/alice-bundle": "0.2.*", // 0.2
"jms/serializer-bundle": "0.13.*" // 0.13.0
},
"require-dev": {
"sensio/generator-bundle": "~2.3", // v2.5.3
"behat/behat": "~3.0", // v3.0.15
"behat/mink-extension": "~2.0.1", // v2.0.1
"behat/mink-goutte-driver": "~1.1.0", // v1.1.0
"behat/mink-selenium2-driver": "~1.2.0", // v1.2.0
"phpunit/phpunit": "~4.6.0" // 4.6.4
}
}
APITestCase for Symfony4 seems to break. I'm getting user not found. It seems to be still hitting my app database instead of app_test database.