Test framework similar to catch?

Hi there!

I’m completely new to rust and I am still going through the book. Will probably use it for a rewrite of one of my pet projects, which was previously in C++.

I’ve grown fond of a particular C++ test framework called Catch: https://github.com/catchorg/Catch2

The nice thing about it, is that you can recursively layer tests on the same objects (being reset, since they’re passed by-value into each test) so you get some very clean-looking and intuitive tests which read a lot like specs, instead of having a lot of completely separate test methods with all the noise of setting up that usually follows.

For instance (taken from their Getting Started Page):

TEST_CASE( "vectors can be sized and resized", "[vector]" ) {

    std::vector<int> v( 5 );

    REQUIRE( v.size() == 5 );
    REQUIRE( v.capacity() >= 5 );

    SECTION( "resizing bigger changes size and capacity" ) {
        v.resize( 10 );

        REQUIRE( v.size() == 10 );
        REQUIRE( v.capacity() >= 10 );
    }
    SECTION( "resizing smaller changes size but not capacity" ) {
        v.resize( 0 );

        REQUIRE( v.size() == 0 );
        REQUIRE( v.capacity() >= 5 );
    }
    SECTION( "reserving bigger changes capacity but not size" ) {
        v.reserve( 10 );

        REQUIRE( v.size() == 5 );
        REQUIRE( v.capacity() >= 10 );
    }
    SECTION( "reserving smaller does not change size or capacity" ) {
        v.reserve( 0 );

        REQUIRE( v.size() == 5 );
        REQUIRE( v.capacity() >= 5 );
    }
}

Nice and clean. Sections can be inserted recursively to cover continously more specialised edge-cases, without them effecting the copies of the data passed to sections further down the line.

Do you know if anything like this exists for Rust?
If not, I might have a go at making it myself as an exercise. But would there be problems from the ownership model in Rust, since memory is being copied (and mutated) a lot here?
Can you hook new test home-built test frameworks into the cargo test suite?

Thank you a bunch in advance!

Best Regards
Dan True

That example looks like it wouldn’t need a framework. Just replace REQUIRE with assert! and put everything in a function. You could either eliminate the SECTIONS or replace them with println! calls. And then mark the function with #[test] as usual.

I guess I may have misunderstood what the example does (which is a problem with the framework). Just clone anytime you want to modify a copy of an object. Thus what your test is doing becomes explicit.

Thank you! It may just be that easy.

A framework is mostly to avoid having println! and clone statements everywhere. Had my old project continued in C++, I would have ended up with perhaps a few hundred of these test statements for various edge cases, so that sort of clutter matters.

One thing the sections do, is that they can be applied recursively with every change made at the top holding true for all sub-sections, but not for the next section. But if Rust has no problem with cloning & ownership, there shouldn’t be a problem - everything beyond that is just syntactic sugar.

  • Dan

I’d think you could use nested braces to achieve much of the same effect.

let v = ...;
assert...
{
   let mut v = v.clone(); // or even give it a new descriptive name
   mutate v and test
}

Regarding the clutter of printlns, I’d say the clutter of sections is the same. My guess wouldbe that once you’re used to it, having the copies be more explicit will start seeming like more of a bonus. Depending on how hard the objects are to create, you may find yourself preferring smaller independent functions, or using a helper function to create state for tests (both of which I’ve done).

3 Likes

Thank you for your help and suggestions. I’ll start simple and see what I end up with.

  • Dan