Advanced GraphQL, v2

Errors & Testing Solution: Mutation

Scott Moss

Scott Moss

Superfilter AI
Advanced GraphQL, v2

Check out a free preview of the full Advanced GraphQL, v2 course

The "Errors & Testing Solution: Mutation" Lesson is part of the full, Advanced GraphQL, v2 course featured in this preview video. Here's what you'd learn in this lesson:

Scott live codes the solution to the errors and testing exercise, and builds a mutation test in this first part of the solution.


Transcript from the "Errors & Testing Solution: Mutation" Lesson

>> What we want to do to write some tests already had an example of queer actually let's start with the arrows first. Let's do that. So I know that we have in the alt file, yes, I never went where's my solution or branch of my on my stash I did it get stash.

So for the authenticated I'll just write it again. So, if not context.user then instead of throwing a new error here we'll just throw a new authentication error from I guess I got to require the plot. Let's do that. So we got authentication equals acquire Apollo server. There we go.

Grab that. And then we come out here and say new authentication error and for me to say not authenticated. Not all that's good enough. Turn next with these things, there we go. Oops! Cool! And then for authorize is pretty much the same thing. So if context.user.role does not = role, then we can, let's return that right quick.

Oops! We can just throw a new error here. Authentication error and we can say Incorrect roll or whatever you want, you wanna put some more context in there and show them what role they should be. You can do that. Whatever you want, everybody get on that. It's pretty simple.

Throw those arrows in there. Let's check our resolve to see if you got any other errors being thrown in here. We can update those as well for new, new keyword here. So for the better, there we go. So on sign up looks like I have if you sign up and there's already a user there, I'm throwing a new era here for you can't do that.

I would say, me for this one, this is probably another authentication error too. So, maybe import that from up top. I guess you might even say, this is an inputter. I mean, there's really no wrong or right way, it's really up to you. So throw an AuthenticationError, and you might because a user already exists within now you're exposing so your people that hey, this email exists on our database or maybe not say that.

So this be, you know, something valid. Never understood why apps say that sometimes this user already exists, like, all right, I'm just gonna go take Elon Musk's email and go sign up for this app and see if he sign up for it and they'll let me know if he's there or not.

It's not a good idea. So, on sign in, if not a user, then then this might be a bad user input. You can also just say, authentication as well and just be like, invalid, password, plus email, combo or whatever. That's pretty safe. So cool. I don't think we have any other errors in here.

Was this one post so not read ID does not equal that. So this might be another one. And that's not yours, not your ID. Okay, got any other here looks good all right. Any questions on errors? No. And like I said, when you turn node environment to production, Apollo removes the structures from the errors for you.

So in production, when you deploy, a node environment is probably gonna be a production anyway. So the stack tries to be removed for you so you don't have to mainly do that in that format error. Function if you don't want your stacktrace is there. Cool! Let's see. So for directives, sorry, not the participant out there yet.

Test. So for tests, we have these query helpers I'm sorry, this this helper here that create a test servers for us and a client and all that stuff. So what we can do is make another test for a query and another test for mutation. So I'm just gonna make a new file here for mutation that test.js and I'm basically just going to copy everything that's in this query file and paste it into my mutation file.

Changes describes the mutations. And then I'm going to go find a mutation that I want to test. So my type deaths and I'll test the create a post mutation. Let's try that. So if we create a post, we can go back to our test. Change that to create a post.

Okay, new creator create a post and know that's going to use a, okay, where's the app, take this. Create post takes a new post input, which looks like an input with a message. That's basically it. Super simple. So we can hard code that message in the query or we can make it dynamic.

So you'll see what I mean. So if I wanted to hardcoded can just use the shorthand mutation thing like this and just hardcoded in here. Which takes like inputs and put some message, so I could just hard quote it like that. That's cool but if you want to dynamically pass it in a test and you will the use variables parameter after mutation to do it but I was gonna hard quote it for now.

It's not like I really care about it. Message, as always be Hello. And then I'll get back, the message like that. Cool! So we have a creative post. I'm going to get back to mutation here or mutate and not query. And then I need to mock out all the stuff that this resolver is going to expect.

So if I go to the resolvers for creating a post on the mutation, it's expecting this input here. So that should be there because we pass it in as an argument. So we should be good there. We also have this context or here's what we need to mock out.

So you to mock out a user, and it means a mock out models and we basically. Yeah, that's basically all we want to do. So what I'll do is I'll go back to here, models we have, I'm gonna say user and it looks like it's using, createOne. So I'll mark out createOne, and pops up stuff.

We can ignore that for now. See I will mark createOne and then it was returned one. So let's say findMany. That's got to be createOne. I'll say createOne is just gonna return, I mean literally anything that I want. So I'll say it's a function and it's gonna return an objects with a message on it that's going to look like that.

So it's all the same, cool. The next thing is I need a user here which doesn't really matter, but I'm just gonna return URL, anyway cuz I'm not really using it. Cuz, I'm just marking it out because if we go look at the resolver, it's using the author here and the user ID, but I overrated this to do something else, I'm completely ignoring it.

So it doesn't matter. But this is where you might want to put a spy or you might want to spy on this to make sure the right inputs were passed to it to make sure it's good to go. You don't really care what it returns you just you're interested in what it receives because that's what this was lovers responsible for its responsibilities passing in the correct arguments.

So you want to spy on it to figure those arguments out. You're not so much concerned about what it returns because that's a whole nother entity that's tested separately. So you don't really care what the return value of it is. And that's why we mark it out. And this case, I'm going to mark it out and I don't really care about the input right now.

So having called me tape, we can await need state like this. I'm pretty sure it still takes query and this is gonna be createPost, that's close to go make a run this test so I'll say test. See if anything breaks. Of course it past the first time was there was no snapshot it just works.

So, look at the result of it and yeah, this one looks like it broke because cannot read property. createOne undefined. We're gonna do that. Create one. What did I call it? User, should be posted, there we go. That will be better. Okay, cuz now it's obviously gonna break because it's a whole new object, right?

The snapshot is dead, so you're gonna update your snapshot but I was going quick as just to delete that and write it again, so I'll make a new snapshot. Cool, so snapshots good. We can go look at it and we can see the result is exactly what we thought it was going to be arrows are undefined, whatever deterministic.

So if I were to run out again, it'll work. And just to show you an example of why we have to set that setting and the helpers like if I got rid of. For actually met this true or got rid of it which defaults to true, then it's going to ignore my resolvers and is going to have its own generated resolvers which will probably just, yeah.

So you can see instead of generated this, completely different than what I generated. So it's generating stuff every single time, and every time you generate it's a different value. So it's not deterministic, so you can't really use those values for things like Snapshot testing, because the values will be different every single time you run them.

So that's why we have to have that flag there to say like, wait, hold on, not the whole stuff, because I'm going to mark some things out. But there are scenarios where you don't really care what the values are. For instance, if you need a resolver to work, but you're not testing the value of it, then yeah, you will let that mark.

But if you're testing the output of it, and if it's the deterministic, then you will not let the mark having cuz it's gonna be generated every time.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now