Mocking in Rust with conditional compilation

When writing automated unit tests for your application you will probably need to use mocks at some point. Classical object-oriented programming languages such as PHP solve this with reflection where mock object types are created during test runtime. The code under test expects a certain interface or class and the test code passes mock objects that implement the interface or are a subclass.

Similar approaches exist in Rust where mock objects are used to test code that expects a trait type. There is a wonderful Rust mock framework comparison by Alan Somers that lists their features. The biggest problem with most of them as far as I can see is that they cannot mock a foreign struct you are using in your code. Rust does not have a concept of object inheritance for structs so there is no way to mimic a struct type from the standard library or an external crate.

Benchmarking a Rust web application

Performance testing is an important part when developing a network application - you want to know when you have a regression in request throughput in your service.

I set out out my goal 9 for Rustnish:

Write benchmark code that compares runtime performance of Rustnish against Varnish. Use cargo bench to execute the benchmarks.

The basic idea of a performance test here is to send many HTTP requests to the web service (the reverse proxy in this case) and measure how fast the responses arrive back. Comparing the results from Rustnish and Varnish should give us an idea if our performance expectations are holding up.

Testing memory leaks in Rust

Rust has many built-in concepts for memory safety, but it cannot prevent application level logic errors that take up system memory. An example would be a server application that stores something for each incoming request in a growing collection or list. If the program does not clean up the growing list then it will take up more and more server memory - thereby exposing a memory leak.

While working on my reverse proxy project I discovered such a leak in the HTTP library Hyper. In order to prevent and detect memory leaks in the future I set out my goal 7:

Add an integration test that ensures that the proxy server is not leaking memory (growing RAM usage without shrinking again). Use /proc information to compare memory usage of the current process before and after the test.

Static variables made thread-safe in Rust

When writing integration tests for my Rustnish reverse proxy project I have hard-coded port numbers in tests. This is not ideal because it is hard to keep track of which port numbers have already been used and which ones are available when writing a new test. Because Rust's test runner executes test cases in parallel it is important to coordinate which test uses which ports so that there are no clashes that break the tests.

One obvious solution to this problem would be to disable parallel test execution with cargo test -- --test-threads=1. But we want to cover program and test isolation with our test so this is not really an option.

Replacing unwrap() and avoiding panics in Rust

unwrap() is a useful tool in Rust but is also bad practice in production code that should not abort with unpredictable panics.

Therefore my goal 4 for Rustnish is full integration tests with no panics allowed:

Expand the integration tests to confirm that the reverse proxy is working as expected. Add tests with broken HTTP requests to cover error handling of the reverse proxy. All unwrap() calls in none test code should be removed and covered by proper error handling.

Converting a Hyper server to Tokio

Since my first blog post where I constructed a server with Hyper some time has passed and there is now a new version of the library that is based on Tokio. My goal 3:

A new version of the Hyper library has been released which is based on the Tokio library. Convert the existing code to use that new version and provide one integration test case.

Tokio handles input/output asynchronously, which makes setting up a server more complicated. The benefit is more efficient parallel execution with a non-blocking event loop.

Using Visual Studio Code for Rust on Ubuntu

I already wrote about using Eclipse for Rust development but after trying Visual Studio Code (VSCode) I think it is slightly better than the Eclipse integration:

  • when the cursor is at a variable the same variable is highlighted elsewhere.
  • tooltip popups when hovering over variables, functions, methods.
  • better native support for Git and Markdown files

Syntax highlighting, autocompletion, Ctrl + Click on functions and auto-formatting of course also work in VSCode as you would expect from an IDE.

Writing integration tests in Rust

In my first post I wrote a quite fragile, minimally working prototype that uses many unwrap() calls thereby raising lots of panics during execution. Implementing and verifying proper error handling requires testing. I don't want to do unit testing yet because that would require research about complicated mocking techniques and dependency injection in Rust. Instead, I would like to do integration testing of the whole application to prove that the end result is working as expected.