Testing GraphQL (Jake Dawkins)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi everybody how's it goin how's it goin this room is wonderful by the way how many people just by a show of hands really love testing like it's it's a wonderful thing you're here because you love it that's great I'm assuming most of those aren't sarcastic what about hate testing like this is the bane of your existence you're here because it's awful and terrible and how many of you have just never tested like unit tests and integration tested I mean you know test in production yeah any above okay cool yeah well my name is Jake Dawkins and I'm here to talk about all of the above except for testing and production hopefully a little bit about myself as Jesse mentioned I'm an Apollo architect I work on the Apollo team and really just what that means is I'm here to help you navigate the graph QL ecosystem in Apollo's tools specifically around some ideas of best practices schema design and testing that's the talk these these are things that we we get special exposure to and unique exposure to working with some really large companies everybody does things differently and seeing how people do things it really gives us insight into how to make things better so hopefully if you haven't had experience with testing especially with Apollo's tooling whether it be on the client or server this will be enlightening and wonderful for you so what am I going to be talking about exactly so I'm gonna go into like a little ideological sidebar talk about the purpose of testing the effectiveness of different kinds of testing and then I'm gonna go a little bit into the weeds and talk about testing your clients and testing your servers so without further ado let's get into some ideology about the purpose of testing so a lot of people raise their hands why do you test for me it comes down to like two main categories there are other reasons you know everybody has their own preferences but I like to think of the testing falling into two categories of safety of your production code because you don't want to test things in production you know have your users be the first ones to find bugs that's generally a bad idea and second thing that I think is often not talked about very much as a documentation I can't imagine alright I can't count how many projects that I've come into and the documentation is kind of lacking for lack of a better term or in case is where I wrote the code there just wasn't documentation and it's been three months and I have no idea what I wrote anymore so where do you look i I think that tests not only are they generally describing in plain text in the title what they're supposed to do they also show you how to use the function and its expected behavior so documentation I think is a really important aspect of tests that people often look over up next talk about the effectiveness of tests so I like to think of testing and the mindset of the Pareto principle and for the 99% of people who don't know or care what that means it just means that most of the time like 80% of the benefits you're gonna get from something come from like 20% of the work in other words like you do 20% of the work you get 80% of the benefit in other words the other 80% of your work isn't really wasted it's just less beneficial than the beginning part so for this talk I'm gonna try to focus around helping you choose what 20% to test especially when it comes to Apollo's tooling I would be I would be a terrible presenter if I didn't mention Kent C Dodds and his wonderful work around testing he just released a JavaScript testing course but he's also done a whole lot more he he proposed this idea of a testing trophy so I mean we've all seen pyramids like food pyramid testing pyramid you know the two most popular ones Great Pyramid whatever and then we have this this testing trophy which kind of breaks up the value of tests especially the return on value by proportion and what you really need to know from this chart here is that your static test your static typing static analysis is gonna get you a lot of benefit especially when it's automatic the next most beneficial part of these tests is going to be integration tests these aren't unit tests which unit tests just test individual functions individual components in isolation whereas integration tests test how these pieces come together I like to think of it as in saying like if you want more confidence in your code you should be testing the parts of your code that you're least confident with or that are most likely to break because these these pieces while they most might be the most the most difficult to test or the most intimidating a test not only in doing so you learn a whole lot about it you will also be more confident in your code going into production so for those reasons I'm gonna be talking a lot in this talk about integration testing I will talk about the other forms of testing with regards to client and server applications but integration testing will be my focus here so let's jump off the sidebar and talk about testing clients so when I talk about this I'm mostly meaning testing react Apollo and Apollo clients projects these are using react as the few layer for example v this is what I have the most experience with so this is what I'm gonna talk about these ideas can transfer but this is this is what I'm talking about so first off let's just sort of out static typing and what it means for a react application generally what we're talking about static typing is assigning types to props so your components they get props and you're saying hey this prop is gonna be a number this prop is gonna be an object and defining the shape of that prop ideally if we're talking about react projects that use Apollo and use graph QL what you're talking about is these types coming from your production schema so since graph QL is a strongly typed language we know exactly what we're supposed to be getting back we know when something might be null and might not be there and we also know when they're definitely going to be there and when you can rely on it the the Apollo CLI actually if you saw Jeff's talk this morning he was talking about it and Adam Mary showed some more automatic type generation the Apollo CLI really helps with this especially with your with your production schemas you got you know you can also have the different versions of your schemas tagged differently for dev or otherwise it makes this really easy to do and I'll show more of these examples later on but for now I'm just gonna skip straight ahead the unit testing I'll go through this and then show examples unit testing this is a this is just testing testing in isolation so testing components showing them in isolation and I just like to think of this in react as usually as props in render logic out you may have some small interactions with these items but generally if you're testing something in isolation you're like okay I click the button is it firing the function that's the prop like it's not too in-depth of test with unit testing for this I would highly recommend testing library also by Camp Dodds it is wonderful all the examples that I'll be showing will be using react testing library as kind of a scaffolding for it but yeah moving right along there's also integration testing that you can do and this is really just making sure all the pieces fit together so you have all these units all these tiny pieces what happens when you try to render them together what happens when one component tries to render all of its children does it crash and burn is it passing it the correct props and really this is also important because when you change those things in the future you're also changing potentially how they interact with each other and if you're not careful you break everything in production but there's also more to this not only you have to figure out you know is this rendering the right thing you also have you're loading your error states things that aren't really going to be at the from your mind when you're developing because you know you may have data mocked and it may be right at your fingertips but in production you're gonna have a loading State almost every single time you're gonna have an error State maybe more times than you'd like and these are things that you should definitely be testing for one of the tools that we offer for this especially in the react world is a mocked provider which if you're familiar with react Apollo it works by wrapping your entire component tree in a provider that has access to things like the client and the cache so whenever you're a query and your mutation components later on try to make a query in a mutation they can look at the context and see hey here's our client this is what we're actually using to make requests so we provide this tool to make that a little easier for you next up we have into in testing and this is a little bit outside of the scope of this talks I'm not gonna go too much into depth of this but this is making sure your app is usable starting up the app generally inside of you know like a chrome window or inside of a web environment and making sure you can actually do things like login and then once you're logged in hey can I actually navigate like our my or my authentication header is getting passed around properly to make sure app is actually usable because you know unit tests are great and all but if the app doesn't start up or the user can't log in that's a pretty big problem cyperus is a tool that's really great for this it's a really great experience that I've used in the past but I won't be showing example specifically of that today but speaking of examples I will be showing a demo of some the other things and now if I can hopefully get out of this in Mira my displays give me one second lovely get this here get my terminal window back up okie dokie [Music] wonderful okay is that visible can everybody see that pretty well okay kind of yell if not good awesome so here's what we have we have a very simple test that all this is going to do is render a single react component in isolation this is an example of a unit test full react that I was talking about if we want to look at this component real quick because the vs code is wonderful I could jump straight to it this is a very simple component it takes in a site it takes in a rocket and it renders the Rockets name the rocket type and the site that the rocket is gonna be launched from this is actually a code snippet from our full stack tutorial that we published last night and it is available for everybody to go check out so if any of these examples I go through too quickly feel free to hop on this later and check it out it's really good it's all still tested the tests are published and you can go run them for yourselves and mess around them copy paste whatever so yeah back at our test we have this render function that we're importing from test utils which I'll explain in a second but all you need to know is this is the react testing libraries render method and down here whenever actually call this render method I'm passing at our component giving it some props and it's gonna render it to a DOM and then I can make assertions against it like try to get something by text you know the Apollo launch pad the big Falcon rocket one and then bfr because we saw that all three of these things should be rendering and if I hit save on this you can see they rerun I'm not just trying to do some like some magic here and just pretend like tests are passing so yeah that's a very simple unit test rendering a component out not going too crazy with it up next we have something a little more crazy we have a login page which if I look at this one this has an Apollo consumer component which gives us access to the Apollo client so that we can update the cache later on and we also have a mutation component here and we're trying to log in a user essentially the login form is just UI so it's not that important for the sake of this talk all we know is that we're passing it a mutation called login and that login form can then call it whenever the user click Submit so what are we wanting to do here well step one we should wanna make sure that a render is like this is making a mutation so it's not gonna immediately you know make a query or anything like all we want to know here is that it renders first off so that we can actually click the login button right so let's go back to our login example test here because this isn't the full log intestacy so we have this render component still and if we try to render our login page just by itself and I'm gonna unskipable it says could not find client in the context of Apollo consumer and that has to do with the fact that the thing that I mentioned a minute ago is that at the top of your tree Apollo's query and mutation components are always looking for that Apollo provider and if you don't have it it's not you're not gonna have a good time you will get this so on this next test down you can see that I take this much provider that we have which mocks out the client and mocks out the cache so you don't have to manually do that stuff yourself and then you can also pass it an array of mocks which I'll explain in a second but for right now I'm not gonna worry about it I'm just trying to get this thing to render like I said so now if I save this and run this test and then we can check to make sure the login button actually shows up so this is a great and all but there's a little bit more because you know what happens if you click the button does it explode or does it actually log you in so let's check that out we have this much more involved test here login and that that mock provider logic that I had just then like where I'm actually taking mock provider and wrapping the component with it I I just I made a custom renderer in react test render or testing library and it's called read and render Apollo and that's all it's doing is this this login component here is the exact same test as the last our last test I showed you it's just wrapping it's just wrapping this login with a mock provider I just did that to save time considering you know if you're writing a hundred of these things or more than you don't want to have those two lines every single time because that gets really cumbersome really quick so you can see that I'm still like I'm still testing this if I if I were to break this then it would break these tests on the right but this is the same test so what happens if I want to click it well a couple of things so whenever I click it it's gonna fire off a mutation I showed you the MOX array a second ago that you can pass to the mock provider and this is actually this is actually really powerful so what the marks do is they act as a matcher so anytime that this request here gets made in other words anytime this login user mutation gets fired with these variables a great you know this just can return this email the mocked Apollo client instance is gonna return it and your component is gonna see this response you know the data with login ABC this is a login token it's a horrible login token but you know nonetheless so yeah then we get down here to render Apollo we render our login component and we pass it our mocks and we also are gonna pass it a instance of the cache that we just instantiate it right here just just so we can check it later and make sure that the cache updates that I pointed to actually happen so yeah then fire event is something that comes from react testing library so I can fire a change event on the login input type in our super secure email no password because security is unimportant at least for this Abdul quote and then I'm gonna click the login button and what's gonna happen then is the components going to enter the loading State so this mock provider it actually does trigger the loading stating your components so you can test that and then what I'm gonna do is I'm actually just gonna wait until the login button shows back up now in the app like the login button button wouldn't show back up because you're getting redirected back to other parts of the app but when I'm talking about this component find this mutation unless I were to redirect this button would just show straight back up so I'm just waiting for the button to show back up to know that my mutation finished and then afterwards I'm taking that cache instance that I had above and I'm running a query against it just to see if my is logged in was updated and if I change this to like his logins then this would fail and it would say yeah is logged in is not on the type object of Reuters not on route query another was saying this isn't in the cache what are you doing but if I do test it I pull off the is logged-in object from the response of this query and I can check to make sure that is true so in other words the login worked so that is just a really quick run-through there you know quite a few more examples of this in the project that you can go look at yourself but for the sake of right now that's all we're gonna look at and please give me a moment while I unmerry my displays this might be might not work very well where did it go yeah there's Google Chrome come back thank you alright now on to testing servers this is what I'm actually really excited about because we haven't written an article about this one recently I actually wrote the the testing Apollo or react Apollo has query mutation components a couple months ago so these are things that we've already gotten feedback on already talked about it publicly whereas testing surface is something that everybody kind of has their own idea for right now especially like static typing is one thing that people would generally say it's like yeah this is a really easy thing to do right I mean you have your static types you know what your resolver is gonna be returning right but then it gets more complicated in that type generation for resolvers especially isn't that straightforward at least not to do automatically you can definitely type your resolvers but the problem with typing them automatically is that resolvers aren't required to return anything even recognizable to the user until the very leaf so like the very end of the execution is when resolvers are actually expected to return something that's like consistent with the schema or solvers along the way don't have to return anything they can return anything and it's really hard to generate types automatically for that unless you enforce some restrictions saying like an object type has to return a javascript object in the shape of the type that it's returning which you can do like that that's fine but default resolvers also make this hard like if you were to return that from a parent and you just return the correct shape and not provide those child resolvers period then you don't you don't have to type them because they don't exist but you do have to type the parent correctly so you have this weird world where you can kind of do anything you want with the resolvers of next unit testing is actually something that's very enjoyable it it can really leverage graph QL as dependency Injection very well and the reason and I'll talk about like what this is in a second why this is important but at the end of the day testing resolvers and data sources are pretty simple because resolvers are dysfunctions and data sources are just classes so if you've ever tested a javascript function you can test resolvers as long as you as long as you were using dependency injection wisely the the next part of this which is something that's new that we've actually been working on i've been working on for quite a while trying to figure out how people do this right now and how they want to do this is integration testing and what i mean by integration testing is testing the execution of an operation not testing expresses like HTTP semantics logic or anything like that I'm saying give it an operation does it run through all the resolvers properly does one resolve or pass the correct information to the next one all the way down the chain and is the response somewhat regular like we expect to see so a tool that I'll show in a second that we've actually built for this is called a policy server testing you can install it just like you install any other Apollo server instance or integration like a Paula server express happy KOA anything you want so I'll show that in a second but before I forget and in testing is also very possible with this this actually does check the HTTP semantics and all the fun that comes with that you have to provide actual HTTP requests do it and you actually have to start up a real server so I'm gonna show how to do all those things you know easy easy easy said as done let's see however here bring up my terminal once again and cool so this isn't the same project this this tutorial is all in the same project but I'm gonna show you how to test this this login mutation that we just ran the client side so if I actually open up this resolver real quick so you can see what it looks like it's it's a realist it's a relatively short resolver what is doing is it's taking an email argument and it's taking a context object which has data sources on it and this is what I was talking about with dependency injection is that other than buffer at least which is which is a node thing other than that this function this resolver only uses things that are passed to it as arguments directly same thing with every other resolver in this file we're not actually importing anything at the top of this file and using it inside the resolvers because that makes it a little bit harder to mock and most of the time you can pass these things through his arguments whether it be on the context or actual arguments to a graph QL operation so yeah here is just you know looking up a user from the user API data source and if the user is there then it's returning a token back to the user so they can make authenticated requests just let me get rid that so back to this test so this test is just checking to see if it returns a base64 encoded email it's successful because you know these tokens are very secure so what we're doing here is we have these mocks at the top of the file and I'm just tried these are all just function mocks and I'm just mocking the return value of this finder create user which exists on data sources user API dot finder create user it's the same function that the resolver was calling I'm mocking this to just return true once just to simulate hey we looked up a user and they exist I promise and then we're actually just running the resolver so I just imported the resolve the entire resolver map from the other file and I'm just calling resolvers mutation login and I'm passing it nothing for the parent type because it doesn't need one I'm passing in an email super secure email and I'm passing it our mock context which includes this data source and which includes this mock and then all I'm doing is I'm just checking to make sure that the result of that resolver is the correct thing it's that simple and I'm also doing it like an extra step here and I'm saying was the data source actually call with the correct function or the correct arguments you don't have to do that I do that just because I like testing and that's just who I am so love it or not hop over to a data source very similar idea this data source is a rest data source instance it's making requests you know straight to a SpaceX API so for example for this launch API dot get all launches it looks kind of similar I'm taking I'm mocking the actual underlying get function on that data source because I have the instance at the data source so I can just say data source dot get and I can mock that I can override it just for the sake of this test and what that allows me to do is it allows me to still test to make sure that I'm handling a response properly it still allows me to make sure I'm transforming that you know that rest api request properly and then i can just call the data sources method and then i can make sure that it equals our formatted mock launched like it's supposed to this this is a really good pattern and it works pretty well i've i've done this quite a good bit and same thing i can then call make sure that the underlying i get the you know the fetch the fetch implementation is being called with the proper URL this works well but there's another step that we have to go into I promised integration test so here we are this is actually something that's kind of fun I I don't this tool and with the help of people and it's it's really fun to use personally so I'm gonna do it ignore this knock that doesn't it's not supposed to exist because we're not actually mocking the HTTP cost at all so let's take a look at this where did that go I'm not gonna do this first row because it's really commented you can go look at this one laters the integration test file at the root of the repo so yeah let's look at this this is saying fetch is a single launch and what this is doing is a couple of things first off this function here construct test server this isn't part of the Apollo server testing this is just a function that I wrote I'll actually show it to you it's Oh Mike in this area here we are construct test server all it's doing is it's taking our user API our datasource it's taking our launch api or other data source it's instantiating them and just passing them to a you know a class instantiation of a new apollo server this is just if this is just to make sure that we're not having to use like our our our production Apollo server class instance cuz usually an integration test that's not gonna be feasible we want to mock some things so we're yeah we're reinstating a new instance of Apollo server we're passing it our our production type deaths our production resolvers we're passing it a context function that we're either saying default context is like yeah we want to use the server's actual context function otherwise we just want to use and an overwritten one that we can write and then data sources just as if you were in San Shi a ting an actual policy server class we're just passing some data sources along so that's what construct test server does you don't have to have a function for that I just didn't want to write this multiple times so if I go back down to my test construct test server it returns the server so if you said a new Apollo server and whatever the return of that is that's what this is and it returns our two data sources and the reason I did it like this is because I wanted to actually access those data sources so I could do the same thing a mock those underlying bottom level bottom of the stack get calls I can mock those actual in this case those sequal eyes find all mock you know find all functions I can actually mock those if I want to so I don't have to you know actually spin up an in-memory instance of sequel I'd or you know if you're using something you know a little heavier like Postgres you don't have to spin up and instance a Postgres for this I'm just mocking that but leaving the rest in stock but leaving the rest in place so leaving all the data transform logic leaving everything else in place and then here is the part that we exported from the Apollo server testing library it's this create test server so what does this do let's let's take a look great test server no wait yeah this thing this thing keep going yeah so this is a little this is a little more complicated essentially it exports the return of this is two things it's a query and a mutate function and these kind of look similar to Apollo client if you've ever used like the client that I don't like that client dot whatever like client query and you can pass it a query and then you know whatever your query name is this is what that looks like it also allows you to pass variables along which you will see in just a second I hope I didn't break anything by editing that file but we'll see cuz live coding is great let's get back down to it yeah so I'm getting query from that create test client and what this actually does is some of the things talked about this morning with Apollo server 2.2 is a new request pipeline and Martin wall are gonna be talking more about that tomorrow but this is a new execution model for Apollo server and how it actually runs queries so we actually added in a utility if you saw it at the top of that file I don't think it's still open now called execute operation to the base class of Apollo server so you can actually call queries directly from the Apollo server class so that's what this is doing is it's running an operation against the class running everything through the real request pipeline giving you exactly what you should get from Apollo server so then if I take this query and I run an actual query against it I'll get launched query for this example is just up above here query launch again you know it gets passing variables through has an ID it has it is booked which is actually looking up from the sequel ID database we have a rocket you know it's it's not a trivial query it does a couple things from multiple different data sources and if I where'd that go yeah if I run this test it'll actually write it a snapshot because I didn't feel like writing these tests in line but if I go look at it snapshots integration book trips fetches list of launches I wrote a lot of these because they're fun single launch this is what it actually returns it returns data and data is an object that has launch ID is booked true on it because this is actually something that was written to the database it returns the mission with the Falcon the Falcon satellite and it returns the rocket so this is this is as good as you're gonna get with integration testing it hooks directly into a polish server it gives you exactly what you want with the exclusion of the HTTP semantics and the actual rest lookups through the actual database looks up lookups this is everything in the middle so what if you wanted to do everything else what if you wanted to test with real queries this isn't part of a public server testing mostly because there are a lot of a public server integrations and I didn't want to write one and maintain one for every single one of these because there's a little bit different for every single one but I have this start test server function that I wrote and you can do the same thing this it's not a lot of code so if you want it to copy and paste it not that bad I left in the code here if you're using Apollo server express the top lines that you would do but for example what this does is it takes an instance of Apollo server this can be your production instance if you want especially if you have a staging environment set up with your databases with your services if you have a staging environment this is all that you need and what it's doing is it's starting the server on a dynamic port so it picks a port and it creates a new Apollo HTTP link and then it actually calls the execute function with that link with your query and variables so this is testing everything that there is to test this is testing the HTTP semantics you can pass it headers this is testing the entire execution opera execution pipeline that we showed in the last integration test this is testing the actual underlying fetch calls this is testing everything that you can test and these tests are actually even shorter than last one like get list of launches all I'm doing is I'm calling this export of graph QL function from that with a query with some variables and then I'm snapshotting the results and I'll show you those to prove that they exist there's a list of launches I forced it page size 1 so it only you know showed me once I'll have a huge snapshot just to show on stage but this is what it is it shows you a launches array and it shows you individual launches with missions mission patches and names because that's all we asked for so let me hop on back to my slides if I can let me see here where'd it go here we go on mirror displays and there we go back to that and back to this delightful ok my parting words is I want to say that testing is essential especially for large companies especially for large projects testing is essential so it should also be easy and it should also be effective and that's that's what I'm here for that's what we're here for we're here to make these tools we're here to maintain them we're here to make your lives a little bit easier so maybe you know a few more of you would raise your hands and say that you like testing so yeah that's that's all I have for you I hope you check out the projects and thank you so much for your time you [Applause]
Info
Channel: Apollo GraphQL
Views: 10,009
Rating: 4.8778625 out of 5
Keywords: GraphQL Summit 2018, GraphQL
Id: loA3FwbVt90
Channel Id: undefined
Length: 30min 45sec (1845 seconds)
Published: Tue Dec 04 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.