We'll write Functional Tests to test an AdonisJS route using the Japa API Client. Learn to test happy paths by asserting successful responses and sad paths by asserting purposefully failed responses.
Within this project, I've got a simple PostsController that mimics requests and responses you might make for a resource. This will allow us to get comfortable without actually needing database integrations quite yet.
To start, we'll make a posts spec file.
node ace make:test posts --suite=functional
Copied!
Then, let's test a GET request to our posts.show route definition! Remember, our AdonisJS server boots in functional tests, so that means we can also use the router to generate a route path by its identifier.
test.group("Posts", () => { test("fetch a single post with the provided id param", async ({ client }) => { const route = router.makeUrl("posts.show", { id: 1 }); const response = await client.get(route); response.assertOk(); response.assertBodyContains({ id: 1, title: "My first post", }); });});
Copied!
Here we'll use assertBodyContains so that we can assert only a portion of the body, noting that it also contains a summary.
Great, so we know our posts.show route is working, but it's also always a good idea to test the sad path to ensure it's failing how we expect as well. For example, what if the post we're trying to find doesn't exist?
test("fail to find a post and send a 404 response", async ({ client }) => { const route = router.makeUrl("posts.show", { id: 2 }); const response = await client.get(route); response.assertNotFound();});
Copied!
Perfect, we get a 404 not found exactly as expected. However, if we dump the body, we'll see it might not be exactly what we'd expect.
test("fail to find a post and send a 404 response", async ({ client }) => { const route = router.makeUrl("posts.show", { id: 2 }); const response = await client.get(route); response.assertNotFound(); response.dumpBody();});
Copied!
What gets dumped is the HTML from Youch showing the 404 development error screen. That's because this uses content-negotiation and we have not specified how we'd prefer to get a response with our request. Let's fix that.
test("fail to find a post and send a 404 response", async ({ client }) => { const route = router.makeUrl("posts.show", { id: 2 }); const response = await client .get(route) .header("Accept", "application/json"); response.assertNotFound(); response.dumpBody();});
Copied!
Awesome! Now we're getting the error back as a JSON response. We're in development mode, so Youch is still the one sending this with extra details, like the frames. But this will allow us to now assert our message as well!
test("fail to find a post and send a 404 response", async ({ client }) => { const route = router.makeUrl("posts.show", { id: 2 }); const response = await client .get(route) .header("Accept", "application/json"); response.assertNotFound(); response.assertBodyContains({ message: "Post not found", });});
Copied!
The sad paths are where we expect things to fail, and the happy paths are where we expect things to succeed. Ultimately, the complexity of the code you're testing should drive how many variations of each you write tests for. As you're writing your code, if there's a section or functionality you struggled with, you'll want to ensure those are well tested. Meaning, you're writing tests for the different sad and happy paths that functionality may have.