It has become a summer tradition with my son: we start developing a game together. This time, we thought of creating a desktop game.
Table of Contents
The Bare Tauri Project
npm create tauri-app (you can replace
npm with your favorite JS package manager). I’ve used it to scaffold a basic Tauri boilerplate:
src-tauri folder holds the Rust code. Let’s get inside.
Inside we can see the
icons folder, which holds icons for our desktop app and the
src which holds our app. Our app consists of only one file –
main.rs. Makes sense, as this the beginning of our project.
main.rs is pretty simple:
The first line is a convention meant to help us build for windows.
#[tauri::command] allows us to build API commands for Tauri. It acts as a decorator to add more characteristics to the following code. We’ll get back to it in later posts.
Then I have the
greet function, which is the simple function we’re going to see a lot in this article.
fn main starts the Rust app. It starts the Tauri builder and does some stuff. The important thing here is the
invoke_handler part. You see it adds a handler with
greet? This sets up the API between the client and the Rust server (remember that
I know you want to get to business. Don’t worry! That’s the last time Tauri gets in the way of our story 😉
Now, we came here to test
greet and make sure it does what we want, right?
How to Write and Run Unit Tests in a Rust Project
There are two places where you can add tests in Rust. The module file itself or a test folder inside the module. We’ll talk about this later.
Our test code is going to test that when we call
greet with a name, it returns the expected string with the name. We will write two tests. One that passes and one that fails. Here’s how it looks like. Don’t worry, we’ll dive into the code in a bit 🙂
The code above is how you write unit tests in Rust.
[cfg(test)] is actually “configuration(test)” and it tells the compiler to ignore this on build and run it on test.
The next lines define a module (
mod tests). When you use
super::* that means you “import” all the properties and methods of the current module. This is why I’m able to use
greet in my test functions. Which, at long last, brings us to our test functions.
fn test_greet and
fn test_greet_badly are simple test functions that assert
assert_eq!(greet("Johnny"), "Hello, Johnny! You've been greeted from Rust!"); does exactly what you’d expect: asserts that the output of
greet("Johnny") equals to the string.
Let’s keep it simple and add this whole code to the
main.rs file, just below the
In order to run tests in Rust, we use the command
cargo test. Here are the results:
Assuming you are not overwhelmed by the usage of
cargo so far, let’s continue to the next step: modularizing our code.
How to Modularize Rust Code
Right now, we have our test inside our module. We actually have our
greet function inside the
main.rs file. It looks like this:
That’s fine now, but this will get more complicated as the project grows. Let’s break down the Rust code into modules.
In Rust, modules are either files or folders with a
mod.rs file in them.
So we could extract
greet to a
greet.rs file and then import it in our
main file like this:
main.rs is now much smaller. All of our logic resides in the
greet.rs file. Notice we made the function public (the
pub preceding the function). We import the module via the
mod greet line. Rust expects the file
greet.rs to reside in the same folder as
The line that changed is our invoke handler:
Notice we are using
greet.greet, we now have
greet::greet. Not a big deal 🙂
use command like this:
mod greet; use crate::greet::greet;
Then in our code, we can use
greet without mentioning its module:
Now we have our main file and a way to modularize our code. Our tests are still in the implementation file, though. Let’s fix that.
How to Separate Tests from Implementation in Rust
I mentioned earlier that a module can also be a folder with a
mod.rs file. That means, we can create a
greet folder with
mod.rs file and it will work the same:
Nothing changed except
greet.rs turned into
We can use that in order to extract the tests if we create a module
Our files are going to look like this:
Wow! We got modularization and unit tests working in Rust in like five minutes without any dependencies! That’s pretty awesome, in my opinion. I’m very happy to see nodejs going that way with
Now my project is ready to begin. Because I’m working on the project with my son, it’s going to take a while, but I’ll update about it. We’re practically building a game that will run on a desktop. Hey, you might see our game on Steam one day 😉