URL Shortener with Rust, Svelte, & AWS (3/): Testing

mileswatson

Miles Watson

Posted on September 22, 2021

URL Shortener with Rust, Svelte, & AWS (3/): Testing

In the last post, we created a simple URL-shortener API and tested it manually with curl and a browser. In this post, we will use Rust's integrated unit-testing features to allow testing with a single command, and then automate testing with GitHub Actions.

Creating a test module

In Rust, we can create unit-tests which allow us to test individual parts of our application. To get started, create a module called tests in your main.rs file, and add the cfg(test) macro.

#[cfg(test)]
mod tests {
    // ...
}
Enter fullscreen mode Exit fullscreen mode

Now we have created a tests module, we can add tests inside it with the test macro.

#[test]
fn simple_demo_test() {
    let x = 1 + 1;
    assert_eq!(x, 3)
}
Enter fullscreen mode Exit fullscreen mode

By running cargo test, you should see that the test fails as expected:

running 1 test
test tests::simple_demo_test ... FAILED
Enter fullscreen mode Exit fullscreen mode

By changing the 3 to 2 and rerunning cargo test, you should be able to see it pass as expected.

Valid Request Testing

The Rocket crate includes functionality which allows us to write unit tests for our applications (you can find the full docs here.

In particular, we will use the rocket::local::blocking::Client struct to simulate requests to our API. We will first use it to check that valid requests are accepted correctly.

#[test]
fn valid_requests() {
    // ...
}
Enter fullscreen mode Exit fullscreen mode

First, we create a client that is able to make requests to our Rocket.

let client = Client::tracked(rocket())
    .expect("valid rocket instance");
Enter fullscreen mode Exit fullscreen mode

Next, we make a POST request to shorten a url, and check that the response status is Ok.

let response = client
    .post("/api/shorten?url=https://duck.com")
    .dispatch();
assert_eq!(response.status(), Status::Ok);
Enter fullscreen mode Exit fullscreen mode

We can then attempt to extract the key which was returned from the response body, panicking if the key could not be parsed successfully.

let key: u32 = response
    .into_string()
    .expect("body")
    .parse()
    .expect("valid u32");
Enter fullscreen mode Exit fullscreen mode

Now we can make a GET request to the shortened URL, and check that the response is a redirect to the original URL.

let response = client.get(format!("/{}", key)).dispatch();

assert_eq!(response.status(), Status::SeeOther);

let redirect = response
    .headers()
    .get_one("Location")
    .expect("location header");

assert_eq!(redirect, "https://duck.com")
Enter fullscreen mode Exit fullscreen mode

Running the full test with cargo run should result in a successfull pass.

Invalid Request Testing

In the current edition of our API, there are two ways that a request can fail - we should check these both.

First, we will create a test to check that a missing URL parameter throws an error.

#[test]
fn empty_url() {
    let client = Client::tracked(rocket())
        .expect("valid rocket instance");
    let response = client.post("/api/shorten?url=").dispatch();
    assert_eq!(response.status(), Status::BadRequest);
}
Enter fullscreen mode Exit fullscreen mode

And finally we will create a test to ensure that the server returns Not Found to an invalid shortened URL.

#[test]
fn invalid_url() {
    let client = Client::tracked(rocket())
        .expect("valid rocket instance");
    let response = client.post("/123").dispatch();
    assert_eq!(response.status(), Status::NotFound);
}
Enter fullscreen mode Exit fullscreen mode

Both these tests should pass without issue.

GitHub Actions

Now we have created our tests, we can write a GitHub actions script to automatically run our tests whenever we push to the main branch. Create a file with the path .github/workflows/test.yml, and add the following script:

name: Test
on:
  push:
    branches: [main]
env:
  CARGO_TERM_COLOR: always
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Build
        run: cargo build
      - name: Run tests
        run: cargo test
Enter fullscreen mode Exit fullscreen mode

When you push your code to GitHub, you should be able to see your tests run (and hopefully pass) in the "Actions" tab of your repo.

alt text

If you have an issue, you can compare your code to the part-3 tag of my repo.

That's all for this post! In the next one, we will work on containerizing our application with Docker. Make sure to click the "Follow" button if you want to be alerted when the next part is available!

Footnote

If you enjoyed reading this, then consider dropping a like or following me:

I'm just starting out, so the support is greatly appreciated!

Disclaimer - I'm a (mostly) self-taught programmer, and I use my blog to share things that I've learnt on my journey to becoming a better developer. Because of this, I apologize in advance for any inaccuracies I might have made - criticism and corrections are welcome!

💖 💪 🙅 🚩
mileswatson
Miles Watson

Posted on September 22, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related