October 15, 2024

Automating API endpoint testing with Spin

Andrew Steurer Andrew Steurer

spin sqlite github actions ci ci/cd hurl test testing api endpoint hurl

Automating API endpoint testing with Spin

Did you know the Spin CLI has a built-in SQLite database? Not only is it convenient to test SQLite databases and database clients locally, it simplifies running automated API tests.

What is Spin?

Spin is an open-source framework for building WebAssembly applications, and has a ton of amazing features - including portability (a single binary can run on many architectures), and sub-millisecond (0.51ms) response times, which means an application can serve requests without any noticeable delay or cold start. See our docs for more details.

Why would someone want to automate API tests?

One reason you might consider automating API tests is increase the quality of the code. Automating API tests ensures consistency in the testing process, as automated tests are less prone to human error than manual testing. This consistency can lead to more reliable and accurate results, which is crucial in maintaining the quality of your API over time. Automated tests can also be executed more frequently, enabling you to catch issues earlier in the development cycle, which can save time and resources in the long run. Finally, with automation, you can easily integrate your tests into a continuous integration/continuous deployment (CI/CD) pipeline, ensuring that your APIs are always validated before they go live.

Another reason you might consider automating API tests is to document and reduce the toil of repetitive tasks. By nature of coding an automated solution, you are documenting a step-by-step procedure, which can be very helpful when someone new is reviewing the process, or when someone has to review the code after a while away from the process. Moreover, any time you can reduce toil (work that people don’t like to do), it improves developer experience by freeing up time and mental resources to focus other tasks.

How to run API tests on a local Spin app

We have some example code that you can clone: https://github.com/fermyon/api-testing-demo

Requirements

  • Latest version of Spin
  • Latest version of TinyGo
  • Latest version of Go supported by TinyGo
    • This can be checked by running tinygo version. At the time of this post’s publishing, the latest supported version of Go is 1.22.
  • Latest version of Hurl

Building and running the application

Let’s start by building the Spin app. In your terminal, navigate to the root directory of the api-testing-demo and run the build command:

spin build

Once the build has finished, we can run the Spin application with a SQLite database:

spin up --sqlite @schema.sql

Once the application is running, open a new terminal window and navigate to the root directory of the api-testing-demo, and try interacting with the API:

  • Create some new users:

    curl --request POST localhost:3000/user/user1
    curl --request POST localhost:3000/user/user2
    curl --request POST localhost:3000/user/user3
    
  • List all created users:

    curl localhost:3000/all_users
    
  • Get a user by their ID:

    curl localhost:3000/user/1
    
  • Delete a user:

    curl --request DELETE localhost:3000/user/1
    

Keep in mind that the SQLite data is stored in the .spin directory. Because we have inserted data into the database, this is going to prevent our tests from succeeding, so be sure to delete the database once finished trying out the API:

rm .spin/sqlite_db.db

Testing the application

We use Hurl to automatically test the API endpoints in this application, and these can be found in the test.hurl file in the root directory of the code.

To break down the syntax of a Hurl test, let’s examine this block of code:

GET http://localhost:3000/user/1
HTTP 200
[Asserts]
jsonpath "$.id" == 1
jsonpath "$.userName" == "user1"
jsonpath "$.email" == "user1@example.com"

You’ll notice we have our HTTP method GET, and the URL of the endpoint we are trying to reach. Below this is HTTP 200, which validates whether the application returned an HTTP 200 response code. Once the HTTP status code has been validated, we then proceed to check the body of the response to see if it matches what was expected. Each line of code below [Asserts] is validating a JSON field. In this case, we are expecting a JSON response that looks like this:

{"userName": "user1", "id": 1, "email": "user1@example.com"}

Now that we understand how a Hurl test works, let’s run all of the Hurl tests that we have in test.hurl. Let’s run the Spin application:

spin up --sqlite @schema.sql

While this is running, open a new terminal window and navigate back to the root directory of the code. To run the tests, you can run this command:

hurl --test test.hurl

If you get an error that looks like this, you forgot to delete the database, so run the command rm .spin/sqlite_db.db to fix it:

error: Assert status code
  --> test.hurl:6:6
   |
   | POST http://localhost:3000/user/user1
 6 | HTTP 202
   |      ^^^ actual value is <400>
   |

Otherwise, you should see this output:

test.hurl: Running [1/1]
test.hurl: Success (12 request(s) in 20 ms)
--------------------------------------------------------------------------------
Executed files:  1
Succeeded files: 1 (100.0%)
Failed files:    0 (0.0%)
Duration:        20 ms

This means that the test was successful.

How to run API tests on a Spin app in Github actions

Running automated tests in Github actions is very similar to running tests locally. If you examine the Makefile and .github/workflows/main.yaml files, you’ll see that we build, run, and test the Spin app. In fact, if you navigate to the root directory of the code in your terminal and run make build, make run, and make test, the tests will work the same.

We don’t have the ability to open a new terminal window in Github actions, so we have to run the spin up command in the background and redirecting the stderr and stdout to /dev/null:

spin up --sqlite @schema.sql > /dev/null 2>&1 &

Once we finish running the tests, we need to stop the Spin app from running, so we’ll kill the process running in the background. The &>/dev/null is supressing any errors we might get from the pkill command, which makes it easy to use the make rm command locally:

pkill -f 'spin up' &>/dev/null

Looking at the .github/workflows/main.yaml file, we can see that Github actions runs the make commands to test the application (after the proper software is installed):

- name: Build Spin app
  run: make build

- name: Run Spin app
  run: make run

# Waiting for the Spin app to be ready to receive HTTP requests
- name: wait for 1s
  run: sleep 1

- name: Run Hurl tests
  run: make test

- name: Stop Spin app
  run: make rm

If you are looking to automate the testing of your Spin APIs, using Hurl and Github actions is a great way to do this. If you are curious about other integrations Spin has to offer, see our API guide. If you have questions or comments, we would love to hear from you on our Discord.


🔥 Recommended Posts


Quickstart Your Serveless Apps with Spin

Get Started