[0:00] in this one we're going to have a look [0:01] at testing our api so we'll get a nice [0:03] test setup created we'll have a test [0:06] database we'll pull in some packages [0:07] which are going to help us [0:09] make those requests or simulate requests [0:11] to our api and also [0:13] make assertions against the schema or [0:15] the json which we get back so the first [0:19] thing we're going to do is actually set [0:20] up our test database because i think [0:22] it's important to have a separate test [0:24] database not essential but it means that [0:27] you're not polluting your development [0:29] database with test [0:31] data and also it means we can have a [0:33] setup which starts in the exact same [0:36] state for every test so that you don't [0:38] have other developers are like adding [0:40] new records in there which make your [0:42] test fail things like that choose high [0:44] definition for the best viewing [0:46] experience and if you'd like to join a [0:48] growing group of software developers and [0:49] take your skills to new level all you [0:51] need to do is subscribe click the little [0:53] notification icon and welcome now you [0:56] may or may not be familiar with symphony [0:58] already but i'll show you how the test [1:00] database [1:02] setup works in there so if we go back [1:03] and have a look at our emv file where we [1:07] determined our database url here and we [1:10] said that our database name will be [1:12] products api if we go over to the config [1:15] folder here [1:17] and in packages [1:19] test [1:20] doctrine dot yaml you'll see that we [1:23] have db name suffix so ignore this part [1:26] on the end here this environment [1:28] variable thing but what we're looking at [1:30] is this so what that means it will [1:32] append [1:33] the word underscore test onto the end of [1:37] your database and so we're gonna just [1:39] run a couple of commands and that will [1:41] create us a test database with that name [1:44] so back to our notes the command that we [1:46] need in order to create our test [1:48] database is this one so i'll paste it in [1:50] my terminal and i'll explain this to you [1:53] so symfony console is all the same as [1:55] when we created our development database [1:57] except here after symphony console we're [2:00] saying [2:01] emv equals test and then by doing that [2:05] it will then create us a test [2:07] environment database let's hit go [2:12] okay and so he's saying he's created [2:13] products api test database i'm going to [2:16] go over to table plus and just make sure [2:18] that i have that there [2:21] okay and so here's my products api test [2:24] database obviously there's no tables in [2:26] there yet because we need to go and [2:28] migrate our schema let's go and do that [2:30] next so the next command is this one [2:33] symphony console and again we're passing [2:36] m test and it's just a doctrine [2:38] migrations migrate i'll copy them copy [2:41] that paste that in there [2:43] run that [2:44] and it asks me if i'm sure i'll just say [2:46] yes [2:49] okay and so it's saying that it's [2:51] migrating my database let's go back to [2:53] table plus refresh [2:55] great stuff so now we have a [2:56] manufacturer table and a product table [3:00] the next thing i'm going to do is i'm [3:01] going to composer require some packages [3:04] and they are alice [3:06] uh because we're using simplyflex we can [3:08] just use a short name for these so alice [3:11] symphony test pack symphony http client [3:15] and something called json schema so what [3:17] are these alice is used to generate fake [3:20] fixture data [3:22] symphony test pack includes php unit and [3:24] php unit bridge [3:26] http client is what the api platform [3:30] test client is built on so http client [3:33] is a symphony component for making [3:36] requests and json schema provides json [3:39] schema test assertions okay so let's go [3:43] and pull that in [3:46] [Music] [3:56] the next thing i'm going to do is set up [3:57] some test fixtures so inside of a [4:01] fixtures folder which you see here just [4:03] gonna add a yaml file for the [4:05] manufacturer and one for the product [4:22] and so i'll just paste in something [4:23] which i've previously created should be [4:25] self-explanatory this is saying that [4:27] this is the entity or the model that [4:29] this is based on this is what can be [4:31] used uh to reference um each particular [4:35] record so we'll be able to reference [4:37] manufacturer from our product fixtures [4:39] and then here is how we create each of [4:43] the fields so this is just going to give [4:44] it a company name [4:46] this will just provide some text for the [4:48] description uh it will generate a [4:50] country code for the country code and a [4:53] date time for the listed date so now [4:55] let's go over to our product fixture or [4:58] product yaml file and we'll do the same [5:00] here [5:01] okay so let me just talk you through [5:03] this for it's the same as what we did [5:05] for the manufacturer so again our entity [5:08] is the product and then we're going to [5:10] create a hundred this time so what this [5:11] is is uh we're creating products from [5:14] number one to 100 mpn i've just used [5:18] isbn because that's probably the closest [5:20] thing i can think of that there's no [5:22] no special [5:24] item in faker in order to generate [5:27] mpn so i'm just using that uh name i've [5:30] just gone with catchphrase these things [5:32] aren't really that important description [5:34] just creating a sentence made up of six [5:37] words uh issue date again using the date [5:40] time manufacturer because if you think [5:42] about our products we can't create [5:44] products without manufacturer so what [5:46] this will do is it will just reference [5:49] manufacturers from our manufacturer yaml [5:52] file here so where we're saying [5:54] manufacturer underscore 1 to 10 what [5:57] this will do is it will reference [6:00] one of those for each of the products [6:02] i'll go back to my notes file and so we [6:05] have a command here which will load our [6:08] fixtures that we just created so grab [6:10] this [6:11] and then i'll go and paste this in here [6:16] and again make sure that you are in test [6:19] environment when you run this otherwise [6:21] you'll end up populating your [6:22] development database okay let's hit go [6:28] and i'll ask you if you want to continue [6:31] okay so these notices here there's just [6:33] a deprecation notice i'll show you how [6:36] we can get rid of these for our tests [6:38] but it's not come back with any errors [6:40] so that's telling me that this has [6:42] actually worked first time which is a [6:44] bit of a miracle because [6:46] that has rarely happened before refresh [6:48] okay great stuff so it looks like we [6:51] have 100 products perfect and we also [6:54] have 10 manufacturers we'll just check [6:56] the manufacturer ids on our products yep [6:59] they run one to ten okay so that's [7:02] worked uh perfectly first time great [7:04] stuff we're now ready to actually go and [7:06] start writing our tests i think [7:08] so the folder is tests and in here we'll [7:11] just create one test so i would normally [7:13] split things down into like product uh [7:15] sorry unit test and feature test and [7:17] things like that but i'm only going to [7:18] create one test here because i'm just [7:20] showing you how you can do this uh if [7:23] you're building your own api then [7:25] obviously you'll go and [7:26] put a bit more effort into your folder [7:28] structures and things like that so we'll [7:30] call this products test [7:32] [Music] [7:36] and this will extend a test case [7:38] called api test case [7:42] and that will give us uh some of the [7:44] tools we need so things like being able [7:47] to simulate the http request we'll have [7:49] a client and also some uh api specific [7:53] assertions will be available to us in [7:56] order for all tests to start the same [7:57] state we're going to pull in a little [7:59] trait here called refresh database trait [8:02] and we're just going to have a little [8:04] look in the comments and see what it [8:05] says about this basically saying that it [8:07] purges and loads the fixtures uh before [8:09] the first test and wraps all the tests [8:12] in the transaction that we roll back [8:14] when it is finished so that means that [8:16] each test in our [8:18] products test will start in exactly the [8:20] same state with the same [8:21] fixtures loaded [8:24] okay so i'm going to add a test for [8:25] getting a collection so test [8:28] get [8:30] collection so here we're gonna get a [8:32] collection of products [8:35] and the way i can do that is like this [8:37] so static [8:39] and then [8:41] create client so that will give us our [8:43] http client and then we're going to send [8:46] a request which will be [8:49] a get request so the method is the first [8:52] argument the second argument is the url [8:56] so that will be for us api [9:00] products i can check for a successful [9:03] response like this [9:06] assert response is successful and that's [9:08] all i need to do there i can check [9:11] headers [9:12] [Music] [9:16] so assert response header same [9:20] and so we shall say content [9:23] type [9:24] and i expect it to have this value here [9:27] application for slash ld plus json [9:31] semicolon [9:32] character set is utf-8 utf-8 [9:36] and then i can actually make assertions [9:38] about the body so [9:40] this [9:41] assert [9:43] jason i'm looking for adjacent contain [9:46] so i'm not looking for an exact match [9:48] i'm just making sure that the json which [9:50] comes back does contain a subset which i [9:53] specify [9:54] and so here [9:56] you can pass it in array format and i'm [9:58] just going to paste in [10:00] some work which i've previously done [10:02] because you don't want to see me type [10:03] all this out and so we're asserting that [10:06] our json should contain this subset here [10:09] so it's all the stuff that you've seen [10:11] using postman using the user interface [10:14] thing we're looking at our context our [10:16] id and then we've also got stuff [10:19] regarding our pagination here under the [10:22] hydra view [10:23] and so i'm going to run this test [10:25] initially but i'm expecting this to fail [10:27] because i've not actually set up [10:29] pagination on my product yet so let's go [10:32] and run this [10:33] [Music] [10:38] okay and so look for our failure and [10:41] here we're getting uh incorrect [10:43] pagination because it's just my product [10:46] is currently using in default pagination [10:48] so in order to be able to test that and [10:50] to demonstrate it i really want to [10:52] set up my own pagination so we'll go [10:54] over to product [10:56] and here under our api resource i'm just [10:59] going to add a new one and i'm just [11:00] going to borrow the one from [11:02] manufacturer so attributes pagination [11:04] items per page is 5 which should [11:08] make my test pass so we should now get [11:12] 20 pages in total the last page should [11:14] be 20 because 500 sorry 100 divided by 5 [11:18] should give us 20. let's go and run this [11:22] okay and this time we are green so [11:24] you'll see that we're getting these [11:26] deprecation notices here which i [11:28] mentioned earlier if we go to our notes [11:31] i've actually got a piece or a line [11:34] which we can add to our php unit file so [11:37] go over to phpunit xml dist which you'll [11:41] find in your project root [11:44] and just in here see where you see have [11:46] the php tags and you have these server [11:50] variables just drop that underneath [11:53] there [11:54] and all saying is just disable our [11:56] symphony deprecations helper let's go [11:59] and run this again [12:01] okay so this time we get our one test [12:04] three sessions but we don't have those [12:06] uh annoying deprecation notices in the [12:08] way [12:09] let's go and make an assertion about the [12:11] number of items which are returned so in [12:13] order to do this i'm just going to grab [12:15] a response here [12:20] and what i'll do because we're filling [12:22] up our page a bit is i'll make my font a [12:24] little bit smaller so we'll go with [12:27] 22 [12:30] okay and then just down here [12:33] i can say this [12:36] assert [12:37] count [12:38] because we're counting the items in an [12:40] array [12:41] we should get five items and if i say [12:44] response [12:45] to array it will take our response and [12:47] convert it to an array and then we can [12:49] count certain items and we're going to [12:52] count hydra members [12:56] okay one test for assertions this time [12:59] so i'm happy with that test let's now go [13:01] and test pagination so we'll hit another [13:04] endpoint except this time we'll head for [13:06] uh page two will append page two or page [13:08] three or something on the end of that so [13:13] [Music] [13:15] test [13:16] pagination [13:17] [Music] [13:21] okay i'm gonna go and borrow some of [13:22] this stuff here [13:24] so [13:25] uh [13:26] i'll go and grab it all but we'll just [13:28] edit out the stuff that we don't really [13:30] need all i want to look at here really [13:32] is the keys that we get here for our [13:34] pagination so we're going to make the um [13:38] request we'll get a response i'll not [13:40] bother checking headers we know that all [13:42] that stuff works what we're going to do [13:44] here though is say [13:46] page equals two [13:49] and so that means that some of these [13:50] things will change we're getting back [13:52] page two [13:53] first will remain the same last will [13:55] remain the same next will be three but [13:58] we also want a previous so [14:01] i'll just drop that down there i'll say [14:04] hydra [14:06] previous [14:08] and that will be page [14:11] one [14:12] and i'll not bother doing a count [14:14] so yeah like i say basically i'm just [14:17] looking to make sure that we get the [14:19] correct pagination details back let's go [14:22] and run this [14:26] okay to test five assertions let's just [14:28] change some of these values change this [14:30] to 18 for example just to make sure [14:34] okay and it failed so that's good [14:36] change that back and we'll test [14:38] something else let's create a product [14:43] test create product [14:50] okay so similar to making a get request [14:53] all the uh [14:54] we require the http client we make the [14:57] request that's up this time we're going [14:59] to make a post request so request and [15:02] the method will be post and so we're [15:04] going to need some more information this [15:06] time the url [15:08] will be [15:10] the same so posting to api [15:12] [Music] [15:15] products [15:16] but now we're going to need an array and [15:18] we're going to say json [15:20] and then we're going to pass an array [15:23] of all of the properties all the fields [15:25] which we will which we wish to fill on [15:28] our newly created product [15:30] so i've just come and pasted some stuff [15:32] in there behind the scenes [15:34] all the usual stuff mpn name description [15:37] issue date the only risky [15:40] strategy i'm taking here is i'm making [15:42] the assumption that we have a [15:45] manufacturer with an id of one in our [15:47] test fixtures which we created so i do [15:50] know that i have that however if we were [15:52] to refresh this or run this again then [15:56] because it's a auto increment in id [15:58] would actually start from 11 months so [16:00] it means that i wouldn't have a [16:02] manufacturer id of one so i'm not going [16:04] to change it i'm going to leave it the [16:06] way it is but it's just to let you know [16:08] that this is probably [16:10] could be done better the way you could [16:13] do it better will be to actually make a [16:15] request actually send a request off to [16:18] retrieve a manufacturer and just use the [16:20] um id for the first manufacturer that [16:23] you get back but that's just a little [16:25] hint or a little tip there that you [16:26] could use [16:28] this time our response code should be a [16:30] 201 so this [16:33] assert [16:38] set response status code at the same two [16:41] zero one [16:43] headers should be the same as the last [16:45] test so or the first test so we'll just [16:48] go and grab that [16:54] okay so while i was recording this i [16:56] noticed an issue and that was that the [16:59] issue date wasn't writable which meant [17:01] that we weren't going to [17:02] be able to write that to the database [17:04] when we did this so go over to your [17:06] product and make the issue date also [17:09] writeable by saying product write we'll [17:12] make some assertions about the response [17:14] body so again a cert [17:16] json contains [17:24] and so we're just looking to make sure [17:26] that all these things are correct the [17:27] only thing i've made a bit different [17:29] here is that this is a format that the [17:31] issue date is going to come back in even [17:33] though we sent it off in this format so [17:36] um just something to be aware of there [17:38] let's actually go and run this and see [17:40] how we get on [17:43] okay so we get three tests eight [17:44] assertions so you should be getting the [17:46] gist of this now rather than see me type [17:48] out these tests in full what i'll do is [17:50] i'll just paste them in and explain my [17:52] way through them as we go [17:55] okay this time we're testing that we can [17:57] update a product so test update product [18:00] setup is the same [18:01] client create client we're making a put [18:04] request but this time notice we've [18:06] appended on [18:08] a [18:09] id for the product which we want to [18:11] update whereas when we created a product [18:13] and we just needed to hit the endpoint [18:16] api products this time we need to [18:18] specify which product is going to be so [18:20] i've gone with api products number one [18:23] and then [18:25] we're just gonna update the description [18:27] so here we're saying an update is [18:29] description [18:30] and then check that we get a successful [18:33] response and then we're just checking [18:35] the json which comes back let's go and [18:38] run this [18:41] okay so that's good then we'll do one [18:44] final test to we'll try and actually uh [18:47] trigger some validation errors by [18:50] sending incorrectly formatted uh body [18:53] content or something like that and so [18:55] this is the test that i've created for [18:56] this test create invalid product and [18:59] i've picked this uh case specifically [19:02] because i noted i noticed a mistake in [19:04] my code and so what i'm going to do is [19:06] i'm going to go and fix that but i'm [19:07] going to try without using a test so [19:10] here i'm saying static create client [19:13] we're making a post request to products [19:16] and then we're going to create [19:18] a product and this time i'm passing [19:21] manufacturer null which really we should [19:24] have to pass a manufacturer in order to [19:27] create a product record because a [19:28] product must have a manufacturer but [19:30] let's just go and see what happens when [19:32] i run this [19:36] okay so i'm expecting a validation error [19:39] so here this search response status code [19:42] same four two two however that's not [19:45] what i'm getting back it's saying failed [19:47] asserting that [19:49] the response status code is 422 if you [19:52] notice here the response status code is [19:55] 201 created which means it's been able [19:58] to go ahead and actually create that [20:00] record which tells me that there's a [20:02] problem with my code so the first thing [20:04] i'm going to do is i'm going to go over [20:06] to product and just have a look at this [20:09] and i'm going to put an not null [20:11] assertion in here so assert not null [20:15] we'll drop that in here [20:20] okay and then we'll go and run the tests [20:22] again and see what it tells us [20:26] okay and this time we have another [20:28] failure [20:31] failed asserting that response status [20:33] code is 201 [20:35] on line 68 of products test so what's [20:38] going on here let's go over to products [20:40] test and we're looking at line 68 [20:45] so here [20:46] where we're saying assert response [20:48] status code same two or one that's not [20:51] what we're actually getting we're not [20:52] actually creating uh that record [20:55] and the reason is even though we're [20:57] passing a [20:59] manufacturer's string here we haven't [21:01] actually enabled right [21:03] for manufacturer on our product so what [21:06] i'm going to do is we're just going to [21:07] go and [21:11] add that now [21:13] and we'll tidy this up a bit because [21:15] it's getting a little bit messy we'll [21:16] drop this onto its own line [21:19] okay back to our test let's run it and [21:21] see where we get this time [21:25] and so that tells us that we are getting [21:27] a two or one saying the product has been [21:28] correctly created and it's also being [21:31] correctly created with that manufacturer [21:33] id as well [21:34] and so we'll leave it there hopefully [21:36] that's been helpful you've probably been [21:38] able to see that you could actually [21:40] build your entire application just by [21:42] using tests it means we don't actually [21:44] have to keep going over to postman or [21:46] going over to the user interface we can [21:48] stay in the same places where our code [21:50] lives just write our tests go make any [21:52] changes we need to make to our entities [21:54] to our attributes or things like that [21:56] also when the test lets us know that [21:58] something's going wrong [22:00] if you've enjoyed this video and you'd [22:01] like youtube to show you more of my [22:03] content all you need to do is subscribe [22:05] and click the notification icon each [22:07] week i release a number of new [22:08] recordings if you'd like to be informed [22:10] about my upcoming videos as well as [22:12] receive exclusive content discounts and [22:15] early access to my new videos you can [22:17] join my mailing list by following the [22:19] link underneath this video