Skip to content
Snippets Groups Projects

tarantool-test

tarantool-test is a CLI test framework, written for tarantool-based Rust applications.

Powered by tarantool-runner, it eliminates huge boilerplate associated with functional tests.

Problem: tarantool-module, which is actively used to develop tarantool applications, depends on tarantool symbols as it is, essentially, a wrapper over tarantool API. Tarantool symbols are supplied only if an application is loaded via tarantool runtime. Therefore, in order to run tests that depend on tarantool-module, we have to develop some tarantool runtime and run test suite there. As the number of projects grows, such boilerplate setups migrate from one project to another, keeping bugs and undocumented behavior.

Solution: tarantool-test introduces CLI application that runs test suite in the tarantool environment. The only boilerplate you need to write is a few lines of code to setup your test suite(or use the default one if you ain't interested in before_all or any other callbacks).

Installation

tarantool-test consists of two parts, merged into one crate:

  1. library, which contains utilities you must use in order to prepare your own crate for tests;
  2. binary utility which you install and run just like cargo test

You can install binary utility from crates.io: cargo install --features="bin" tarantool-test. Take a note that we activated feature bin in order to install the binary. Do not activate this feature when you use tarantool-test as a library.

You can also install binary utility directly from the repository:

git clone https://git.picodata.io/picodata/picodata/tarantool-test
cd tarantool-test
cargo install --features="bin" --path tarantool-test

In order to add library part to your project, you simply use tarantool-test as a dependency: cargo add tarantool-test

Tutorial

You can see an example of usage in Makefile, target run-example executes test suite against example crate, which can be found in example subdir.

Now you want to use tarantool-test with your own crate. Following steps will use default test suite:

  1. Add tarantool-test to your Cargo.toml as an ordinary dependency: cargo add tarantool-test;
  2. Ensure your package could be imported from Lua-side of tarantool. It includes:
    1. Mark your crate's lib type as cdylib(see example/Cargo.toml for a solution if you are unsure);
    2. Export appropriate luaopen_* function, for example: luaopen_libexample. For more detailed steps visit tarantool-module;
  3. Mark desired tests with #[tarantool::test] attribute macro. This would allow tarantool-test to collect and run them;
  4. Bind test suite you want to use by using bind_test_suite macro, which is supplied with tarantool-test - place it at the top level of your crate: bind_test_suite!();;
  5. Build your crate: cargo build -p example. Check ./target/debug directory to find out the actual path of shared object you've built. It would be named something like libexample.so for Linux or libexample.dylib for Macos.
  6. Launch your tests by running: tarantool-test test -p ./debug/tarantool/libexample.so. Tests would be executed, and you are done! No need to cleanup anything.

If you got confused by some of the points above, check example crate or read more details below.

Using test suites

You are not limited to the single or default test suite - you can use as many custom suites as you want, or even bind same test suite to the many entrypoints. The important thing is that tarantool-test binary can only run single entrypoint - so if you would like to run many entrypoints, you'll have to use tarantool-test multiple times, at least for now.

In order to introduce new test suite, you implement TestSuite trait for some type. Then you decide which entrypoint you'd like to bind it to, for example new_test_entrypoint, and bind test suite to it. Then your setup would look like this:

struct MyTestSuite;

impl tarantool_test::TestSuite for MyTestSuite {
    fn before_all() {}
    fn after_all() {}
    fn before_each() {}
    fn after_each() {}
}

bind_test_suite(new_test_entrypoint, MyTestSuite);

Then you run your setup by specifying concrete entrypoint you've used: tarantool-test test -p ./debug/target/myproject.so -e new_test_entrypoint.

You can also omit entrypoint name for your custom test suite while binding, then it would be bind to the default entrypoint, which is named "default_test_entrypoint": bind_test_suite!(MyTestSuite)

You can omit test suite type reference, then the default test suite would be bind to the specified entrypoint: bind_test_suite!(new_test_entrypoint).

Take a note, that default test suite is implemented for (), so the line above would have the same effect as this one: bind_test_suite!(new_test_entrypoint, ()).

NOTE: You can't use the same entrypoint twice. For example, this setup fails:

// DO NOT COMPILE!
bind_test_suite!(first_test_entrypoint);
bind_test_suite!(second_test_entrypoint);

..But you surely can reuse test suites many times! For example, this setup works:

bind_test_suite!(first_test_entrypoint, MyTestSuite);
bind_test_suite!(second_test_entrypoint, MyTestSuite);

Details

tarantool-test is backed by tarantool-runner. It executes test setup in the provided by runner tarantool environment, so anything related to tarantool-runner is applicable here. NOTE: you don't need to install tarantool-runner yourself - it is builtin.

Another important dependency is tarantool-module. You must mark needed tests with tarantool::test attribute macro in order to allow tarantool-test to discover them.

What tarantool-test does: whenever you use bind_tarantool_test! macro, it generates tarantool procedure with needed entrypoint name. Newly created entrypoint deserializes input and launches tests with specified test suite.

Binary utility simply wraps tarantool-runner, introducing cleaner interface.