DEV Community

Cover image for How to test server in vanilla Deno?
WJH
WJH

Posted on

How to test server in vanilla Deno?

DISCLAIMER: the method provided in this article uses unstable API of Deno.

Overview

In this article, we will discuss how to test a server in Deno without using any 3rd party library, just vanilla Deno.

Prerequisite

Make sure you are using Deno v1.1.1.

Server

Suppose we have a file named server.ts with the following content:

import { serve } from "https://deno.land/std@v0.57.0/http/server.ts";

const server = serve({ port: 8000 });
console.log("Server listing on http://localhost:8000/");
for await (const request of server) {
  if(request.url === '/404') {
    request.respond({ 
      body: new TextEncoder().encode("Oops")
    });
  }
  else {
    request.respond({ 
      body: new TextEncoder().encode("Hello world!")
    });
  }
}

This file describe a very simple server, which will return "Hello world!" for any request, regardless of the method or URL, but will return "Oops" when that URL is "404".

You can try out this server by running:

deno run --allow-net server.ts

Then visit http://localhost:8000 and http://localhost:8000/404 in your browser, you should see Hello world! and Oops respectively.

Testing

To write the test, we will use:

  1. Deno.test for creating test cases
  2. assertEquals to make assertions
  3. Worker to spawn our server (note that Worker is unstable as of Deno@v1.1.1).
  4. fetch to make request

For our server, mainly we want to test out two things:

  1. GET /404 should return "Oops"
  2. GET any other URL should return "Hello world!"

Below is the code for testing, which should be stored in a file named server.test.ts:

import { assertEquals } from "https://deno.land/std@0.57.0/testing/asserts.ts";


const serverPath = new URL("./server.ts", import.meta.url).href;

// Spawn the server in a worker thread
const worker = new Worker(serverPath, { type: "module", deno: true })

// Wait for a moment to let the server start (this is a little hacky actually) 
await new Promise((resolve) => setTimeout(resolve, 3000));

// Write our test cases, note that we store them in a variable
// This is necessary such that we can keep track of how many test are there
// And so we can terminate the server properly after every test finished
const tests: {
  name: string;
  fn: () => Promise<void>;
}[] = [
    {
      name: 'GET /* should return "Hello world!"',
      fn: async () => {

        const response = await fetch("http://localhost:8000/");
        assertEquals(await response.text(), "Hello world!");
      }
    },
    {
      name: 'GET /404 should return "Oops"',
      fn: async () => {

        const response = await fetch("http://localhost:8000/404");
        assertEquals(await response.text(), "Oops");
      }
    }
  ]

// Utility function to keep track of number of ran tests
let numberOfRanTest = 0;
const done = () => {
  numberOfRanTest++;
  if (numberOfRanTest === tests.length) {
    worker.terminate();
  }
};

// Run the test
tests.forEach((test) => {
  Deno.test({
    name: test.name,

    // Note that we invoke `done` when the test fn finished
    fn: () => test.fn().then(done),

    // We need to set these options to false such that the test can exit with status 0
    sanitizeOps: false,
    sanitizeResources: false,
  });
});

To run the test:

deno test --unstable --allow-net --allow-read server.test.ts

You should see the following output after running the test:

Compile file:///Users/wongjiahau/repos/levo/core/.deno.test.ts
Server listing on http://localhost:8000/
running 2 tests
test GET /* should return "Hello world!" ... ok (13ms)
test GET /404 should return "Oops" ... ok (5ms)

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out (18ms)

Conclusion

The method I presented here might be a little hacky, but it will be great if you're looking to test server without using any 3rd party libraries or framework.
If you have any questions feel free to post in comments. Thanks for reading! :D

Top comments (0)