Thread overview
unit-threaded v0.6.13 - tags, autotags and support for integration tests
May 16, 2016
Atila Neves
May 16, 2016
Meta
May 17, 2016
Marc Schütz
May 17, 2016
Meta
May 17, 2016
Atila Neves
May 17, 2016
Marc Schütz
May 16, 2016
Advanced multi-threaded unit testing framework with minimal to no boilerplate:

http://code.dlang.org/packages/unit-threaded


What's new:

. Tags.

While selecting which tests to run by package or module is definitely handy and mostly what one wants, sometimes there are cross-cutting concerns. Now you can tag the tests with whatever strings you want and use those strings to filter which tests to run:

@Tags("foo", "bar") unittest { /* ... */ }

Running the unittest binary like so:

./ut mypackage @foo ~@baz

Will run every test in mypackage that has the "foo" tag _and_ doesn't have the "baz" tag. Adding tags on the command-line can only filter, so the number of tests run is always <= of what would be run.


. Autotags

Value and type parameterized tests will have tags added to them automatically for their value if the @AutoTags UDA is used:

@Values("foo", "bar") @AutoTags unittest { /*...*/ }

The first instantiation will be tagged "foo" and the 2nd "bar".


. Value-parameterized tests can now have more than one @Values UDA.

If it does, the unittest block is instantiated with the cartesian product of all Values, i.e.

@Values(1, 2, 3)
@Values("foo", "bar")
unittest { /*...*/ }

This produces 6 subtests, one for each combination of the int and the string


. Support for integration testing

I've been using unit-threaded for integration testing and added something that was very useful to me: a file system sandbox. This is especially important when tests are running in threads so they don't stomp on each other. The code should be self-explanatory:

with(immutable Sandbox()) {
    writeFile("foo.txt", "foobarbaz\ntoto"); // can also pass string[] for lines
    shouldExist("foo.txt");
    shouldNotExist("bar.txt");
    shouldEqualLines("foo.txt", ["foobarbaz", "toto"]);
}

By the default the files get written to and read from tmp/unit-threaded/{randomly generated dir name for each test}. Notice the lack of / before tmp. That directory can be changed with Sandbox.setPath.



. Bug fixes

Enjoy!

Atila
May 16, 2016
On Monday, 16 May 2016 at 08:37:48 UTC, Atila Neves wrote:
> with(immutable Sandbox()) {
>     writeFile("foo.txt", "foobarbaz\ntoto"); // can also pass string[] for lines
>     shouldExist("foo.txt");
>     shouldNotExist("bar.txt");
>     shouldEqualLines("foo.txt", ["foobarbaz", "toto"]);
> }

That's a really neat trick and an interesting use of `with`. I remember that variables initialized in the argument list of `with` used to not be destroyed after exiting the scope, I'm guessing that's not the case anymore? I vaguely remember a PR that fixed that.

Anyway, congratulations on the new release!

May 17, 2016
On Monday, 16 May 2016 at 14:39:22 UTC, Meta wrote:
> On Monday, 16 May 2016 at 08:37:48 UTC, Atila Neves wrote:
>> with(immutable Sandbox()) {
>>     writeFile("foo.txt", "foobarbaz\ntoto"); // can also pass string[] for lines
>>     shouldExist("foo.txt");
>>     shouldNotExist("bar.txt");
>>     shouldEqualLines("foo.txt", ["foobarbaz", "toto"]);
>> }
>
> That's a really neat trick and an interesting use of `with`. I remember that variables initialized in the argument list of `with` used to not be destroyed after exiting the scope, I'm guessing that's not the case anymore? I vaguely remember a PR that fixed that.

You surely mean "used to be destroy", right? Yes, that's been fixed.

>
> Anyway, congratulations on the new release!


May 17, 2016
On Tuesday, 17 May 2016 at 09:54:15 UTC, Marc Schütz wrote:
> You surely mean "used to be destroy", right?

Good question... If I write this:

struct Test
{
    ~this() { writeln("destroying Test"); }
}

with (Test())
{
    //Do stuff
}

Will Test's destructor be run once we exit the `with` scope?
May 17, 2016
On Tuesday, 17 May 2016 at 14:22:51 UTC, Meta wrote:
> On Tuesday, 17 May 2016 at 09:54:15 UTC, Marc Schütz wrote:
>> You surely mean "used to be destroy", right?
>
> Good question... If I write this:
>
> struct Test
> {
>     ~this() { writeln("destroying Test"); }
> }
>
> with (Test())
> {
>     //Do stuff
> }
>
> Will Test's destructor be run once we exit the `with` scope?

It did for me on the latest release.

Atila
May 17, 2016
On Tuesday, 17 May 2016 at 14:22:51 UTC, Meta wrote:
> On Tuesday, 17 May 2016 at 09:54:15 UTC, Marc Schütz wrote:
>> You surely mean "used to be destroy", right?
>
> Good question... If I write this:
>
> struct Test
> {
>     ~this() { writeln("destroying Test"); }
> }
>
> with (Test())
> {
>     //Do stuff
> }
>
> Will Test's destructor be run once we exit the `with` scope?

Yes, it was fixed almost two years ago:

https://github.com/dlang/dmd/pull/3855
May 17, 2016
On 05/17/2016 10:22 AM, Meta wrote:
> On Tuesday, 17 May 2016 at 09:54:15 UTC, Marc Schütz wrote:
>> You surely mean "used to be destroy", right?
>
> Good question... If I write this:
>
> struct Test
> {
>      ~this() { writeln("destroying Test"); }
> }
>
> with (Test())
> {
>      //Do stuff
> }
>
> Will Test's destructor be run once we exit the `with` scope?

Should.