Rust for Startest (Part-3 Tests)

Doga Budak
3 min readJan 14, 2024

--

I was studying Rust as always and improving my library here about websockets, https://crates.io/crates/btcturk_websockets and I decided to write some tests. Here I am sharing my notes and some nice tricks that I learned about writing tests on Rust.

In Rust, it’s a common convention to organize tests in a separate module within the same file, typically placed at the end of the file. This allows you to keep the test code close to the code it’s testing, making it easier for developers to find and maintain the tests.

So Iwill start writing tests to my module you can find the source code here as an example.

Basic unit tests

First tests I will be writing will be for the function which defines my API Keys, It will be simple anyway.

To define a function as test you should put #[test] annotation before hand.

#[test]
fn create_api_keys() {

let created_keys = ApiKeys::new("asd", "fgh");
assert_eq!(created_keys.private_key, "fgh");
assert_eq!(created_keys.public_key, "asd");
}

Simple is that, now when we execute cargo test command, cargo will automatically will pick up these functions as tests and executes them.

Rust provides several built-in macros for testing, including assert!, assert_eq!, and assert_ne!. These macros are used to check conditions and report failures if the conditions are not met. Here I only decide to check for values. Normally in javascript I would also maybe type of the data or for some additional keys but hey, this is Rust. I dont need to check those anyway.

How to test async methods

I am using tokio library for my async methods, and I will talk about that one today. And what I like actually about tokio is that, testing is extremely and amazingly easy. I just add/replace #[test] with #[tokio::test] before any test function.

#[tokio::test]
async fn test_something_async() {
let DB = setup().await;

some_async_func(DB).await;
}

Crazy right ? But it is as simple as that.

Integration Tests

Unit tests are brief and capable of testing private code; they test a single module independently at a time. Like any other code, integration tests are external to your crate and only use its public interface. Their goal is to verify that a large number of your library’s components function as intended.

Cargo looks for integration tests in tests directory next to src.

I created under tests/main.rs a fully working integration test.

use btcturk_websockets::{Client, ApiKeys};

#[tokio::test]
async fn general_test() {
let btc_public_key = std::env::var("BTCTURK_PUBLIC_KEY").expect("BTCTURK_PUBLIC_KEY must be set.");
let btc_private_key = std::env::var("BTCTURK_PRIVATE_KEY").expect("BTCTURK_PRIVATE_KEY must be set.");
let connect_addr = std::env::var("BTCTURK_WEBSOCKET_ADDRESS").expect("BTCTURK_PRIVATE_KEY must be set.");
let api_keys=ApiKeys::new(btc_public_key, btc_private_key);
let client = Client::new(connect_addr, api_keys);
let token = client.clone().generate_token_message();
let connection = client.clone().create_connection().await;
let ticker = client.clone().get_ticker().await;
assert_eq!(ticker,());
}

It is an async integration test, it does reach outside of my little environment and actually does something. I am just checking here if my ticker is working and returns me anything I can use. And yes, when everything set correctly my tests are working as expected and not giving me any headaches. I mean thats what tests are for right ?

In conclusion, testing is a fundamental aspect of Rust development that ensures the reliability and robustness of your codebase. By embracing unit testing, integration testing, asynchronous testing, property-based testing, and continuous integration, Rust developers can build software with confidence. Adopting a testing-centric approach not only enhances code quality but also promotes a culture of reliability and maintainability. As you incorporate these testing practices into your Rust projects, you empower yourself to deliver resilient and dependable software solutions. Happy testing!

--

--