Deno: Integration testing

By | June 19, 2020

Tests are fundamentals components of any applications. I’m a great supporter of TDD. In this articles, we will see how to write integration test with Deno. Deno give you a minimalist testing environment, but you can do lot of things with it already. If you are used to Framework like Mocha or Jest, this minimalism can be quite confusing as you will find that some functionalities are currently missing.

Prerequisites

  • Deno installed
  • Oak, with Drash, the only production ready API framework at this time.

Demonstraiton

Let start by creating a app.ts that will return an Application object that can we use in our tests.

    import { Application, Router } from "https://deno.land/x/oak/mod.ts";
    import { UsersRoute } from "./users.route.ts";

    export class OakServer {

        private readonly hostname: string;
        private readonly port: number;
        private readonly app: Application;

        constructor(hostname: string, port: number) {
            this.hostname = hostname;
            this.port = port;
            const router = new Router();
            // Create the Oak Application
            this.app = new Application();
            // Create the User route and associate it with the Oak router
            new UsersRoute(router);
            // Associate the router with the application
            this.app.use(router.routes());
            this.app.use(router.allowedMethods());
        }

        async start(signal?: AbortSignal): Promise<void> {
            console.log("Starting server");
            let listenPromise: Promise<void>;
            if(signal) {
                listenPromise = this.app.listen({ hostname: this.hostname, port: this.port, signal });
                return listenPromise;
            } else {
                return await this.app.listen({ hostname: this.hostname, port: this.port });
            }
        }
    }

Inside the class contructor, we call methods that allow us to associate routes with our Oak router. In this example we associate the method GET('/users').

If we start the server now, we should have it response to the /users endpoint by a list of users.

Integration test

We create the test file test/user.e2e.test.ts

import { OakServer } from '../app.ts';
import {
    assertEquals,
    fail,
} from "https://deno.land/std/testing/asserts.ts";
import axiosd from "https://deno.land/x/axiod/mod.ts";

const hostname = "localhost";
const port = 1447;
const httpUrl = `http://${hostname}:${port}`;

const abortController: AbortController;

async function startServer() {
    abortController = new AbortController();
    console.log("Starting test server");
    try {
        listenPromise = new OakServer(hostname, port).start(abortController.signal);
    } catch (err) {
        console.error(`Server can't start : ${err}`);
    }

}

function stopServer() {
    abortController.abort();
    await listenPromise;
    console.log("server stop");
}

function afterAll() {
    stopServer();
}

async function beforeAll() {
    await startServer();

}

Deno.test("Get all user", async () => {
        await beforeAll();
        const response = await axiosd.get(`${httpUrl}/users`);
        const users = response.data.data;

        assertEquals(response.status, 200);
        assertEquals(users.length, 2);
        assertEquals(users[0]["id"], "1");
        assertEquals(users[0]["name"], "Bob");
        assertEquals(users[1]["id"], "2");
        assertEquals(users[1]["name"], "Alice");

        afterAll();
});

Many things here. Methods startServer ant stopServer handle the start and stop action of the server. We use abortController.signal as parameter of the OakServer.start() method to tell to the Oak server to not return a promise and wait for ending signal to stop. Without this, our test will wait for the never ending promise of Oak server and will not start. To stop server at the end of tests, we call the method abortController.abort() inside the function stopServer.

Next, we have the Deno test itself. This test call the beforeAll and afterAll methods to initialize and stop the Oak server automatically. We also use the Axiosd library, a Deno equivalent of the popular Axios library in NodeJS. This allow us to make http calls on our server.

Conclusion

Through this simple example, we have see how to write integration tests with Deno on a API, handle by Oak. Deno does not actually have hook like mocha or jest, and after reading some maintainers comments, this is by design. But we are successfully implements our on hooks to control our server. The big difficulty was in fact to find the right libraries to use, as the rich NodeJS ecosystem is not currently usable in Deno.

00

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.