11.
Test Fixtures and the PropertyAccess Component
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
Whoops, an error! Please, try again later.
16 Comments
There was 1 failure:
1) AppBundle\Tests\Controller\Api\ProgrammerControllerTest::testGETProgrammer
Failed asserting that two arrays are equal.
--- Expected
+++ Actual
@@ @@
Array (
- 0 => 'nickname'
- 1 => 'avatarNumber'
- 2 => 'powerLevel'
- 3 => 'tagLine'
+ 0 => 'id'
+ 1 => 'nickname'
+ 2 => 'avatar_number'
+ 3 => 'power_level'
+ 4 => 'user'
)
C:\Users\rakib\Site\symfony2-rest\src\AppBundle\Tests\Controller\Api\ProgrammerControllerTest.php:59
avatar_number become avatarNumber ...
in Windows
oh .. got it . done
Hmm, I'm getting an error. I have a situation similar to this: a user has many posts. When it is trying to remove the user, it crashes because of foreign key constraints, which makes sense of course. How do I work around this? :(
Hey Johan!
Yea, this is a classic problem :). So, by default in Doctrine, when Doctrine setups of your relationship in the database, it doesn't add any "ON DELETE" behavior. This means that if you try to delete a row in a table, but there are other records that reference this as a foreign key, it'll fail. And this is a good default, because it's safe. So, you have 2 options to fix this:
1) You can fix it in your test. What I mean is, you can make sure that you empty the posts table before your test starts (so that it is able to delete a user later). Sometimes, I will literally - in my setup() method of my test - empty a few tables manually, with code like this:
OR, you could empty every table in your project. We actually do that in this tutorial. If you look in the ApiTestCase class that I setup for the tutorial, in the setup() method, we call a purgeDatabase() method, which does the following:
2) When it makes sense, an even better solution is to fix this in your application. What I mean is, perhaps it is ok in your app that if a user were somehow ever deleted, that all of that user's posts are also deleted (or maybe not deleted, but their user/owner" set to null. If you feel comfortable doing this, then you'll update your Post.user property to add a JoinColumn:
The other likely value instead of "CASCADE" would be "SET NULL". You'll need to generate a migration for this, since this is a change that affects your database.
Let me know if that helps! This is a really tough issue that I also struggle with - the correct answer depends on your app. I typically try to completely empty my database before each test, but eventually that can slow your tests down. I usually tackle that problem later when/if that becomes an issue.
Cheers!
I was trying your suggestion using the purger, but this actually gave the error. I guess it tries to remove users before removing posts.
I think it would be painful to manually clear tables before starting your test because you need to know and specify the order in which you delete the tables.
In this case I think setting the onDelete to CASCADE would solve my problem, but only if all my FKs have this option, which probably won't be the case. I'm literally trying to clear the entire database.
I'm thinking of just writing a PHP script that temporarily turns off foreign key constraints ("SET foreign_key_checks = 0;"), iterate over all tables and DELETE all rows.
Thank you for your reply, very much appreciated!
Hey Johan!
So I think I have had similar situations where I've hit the same problems and drawn the same conclusions as you! The purger actually calculates the correct delete order to avoid foreign key problems, but sometimes due to circular relationships, it's just not possible. In fact, I just hit the yesterday, and didn't bother debugging it - I just added the CASCADEs (it was a safe enough situation for me to do this). And yes, I've also done the foreign_key_checks thing too :p.
Btw, there is one other interesting solution for testing, which I know others have used, but I haven't ever quite tried: that is to prepare an sqlite database with a known data set (or perhaps, even empty), copy this to the correct location before the test to have it automatically used. Here are some details: http://stackoverflow.com/qu.... Don't use the "in memory" option - that only works if you're using Symfony's internal test client - whereas here we're making real HTTP requests in a different thread (this is my preferred way).
Cheers and good luck!
I notice that there are so many different ways of tackling this haha
For now I just fixed it by adding the CASCADEs. The solution with the sqlite database sounds interesting though. I might try that whenever simple CASCADEs are not possible anymore and the purger breaks :)
Thank you for your time!
If programmer was using an auto increment field as the primary key rather than nickname, how would you know which route to call?
For example /api/programmers/[id field]
Hey Shaun T.
It works the same, the only thing you need is to use as a wildcard a unique property value on your entity, and call it the same, i.e "/api/programmers/{id}"
Probably if you read this section of the documentation you can undestand it better: https://symfony.com/doc/4.0...
Cheers!
Thanks MolloKhan. I was actually referring to how I can get the id of the programmer that has been created so that I can do a GET to retrieve that programmer using that id, hope that makes sense!
Hey Shaun T.
First you need to add that property into your class (update schema, etc)
Then, after you create a programmer you can include in your response, the information about this programmer, or even a link for accessing such information.
Or, you can implement an endpoint for listing programmers, something like "/api/programmers"
Thanks Diego, what I'm confused about is how I would test this. If I create a programmer, how would I then know what ID to use in the URL?
$response = $this->client->get('/api/programmers/[HowDoIGetTheID???]');
Well, there are a couple ways to do it:
- When you create a programmer, you could return the generated id for that programmer
- Making more granular your test, in other words, testing only the "get a programmer" endpoint. You could manually add a programmer into the DB (specifying its ID, or maybe just fetching it by any other field), so then you can use its ID and hit that endpoint.
Hi Ryan, which Guzzle version that you are using for this example? I tried using latest Guzzle version 6.2 and got some error in the History class. I notice that Guzzle make quite a bit of an update on the version 6.
Hey Vincent!
Yep, this tutorial uses Guzzle version 5 - they're always releasing new versions on me! But, if you download the course code for course #4 (https://knpuniversity.com/s... - you can check out the new version of the `ApiTestCase`. I upgraded to Symfony 3 and Guzzle 6 for that tutorial, and updated all that History stuff for the new version :).
Cheers!
Inore my last comment I think I open the wrong ApiTestCase file, all good now. Thanks.
"Houston: no signs of life"
Start the conversation!