Unit tests in D

Tomek Sowiński just at ask.me
Wed May 5 13:35:10 PDT 2010


Dnia 05-05-2010 o 03:24:50 bearophile <bearophileHUGS at lycos.com>  
napisał(a):

[snip]
> 2) Names for unittests. Giving names to things in the universe is a  
> first essential step if you want to try to understand some part of it.  
> The compiler makes sure in each module two unittest tests don't share  
> the same name. An example:
>
> int sqr(int x) { return 2 * x; }
>
> /// asserts that it doesn't return a negative value
> unittest(sqr) {
>     assert(sqr(10) >= 0);
>     assert(sqr(-10) >= 0);
> }

I think it's achievable through mixin templates:

mixin Unittest!("sqr", {
      assert(sqr(10) >= 0);
      assert(sqr(-10) >= 0);
});

and even doesn't poke your eyes like most mixin code:)


> 3) Each unittest error has to say the (optional) name of the unit tests  
> it is contained into. For example:
>
> test4(sqr,6): unittest failure

If Unittest template** above would be something like:

mixin template Unittest(string name, alias fun) {
     mixin("
     void "~name~"() {
         fun();
     }

     unittest {
         writeln(\"Testing "~name~"...\");
         "~name~"();
     }
     ");
}

then you would see the name of the unit test in the stack trace.


> 4) The dmd JSON output has to list all the unitttests, with their  
> optional name (because the IDE can use this information to do many  
> important things).

This is interesting. How the IDE may help knowing the unittest name?


> 5) Optional ddoc text for unittests (to allow IDEs to answer the  
> programmer the purpose of a  specific test that has failed).

When a test has failed I go to the testing code to see what's up. So I'll  
see any non-ddoc comments someone left there.


> 6a) A way to enable only unittests of a module. Because in a project  
> there are several modules, and when I work on a module I often want to  
> run only its unittests. In general it's quite useful to be able to  
> disable unittests.

... or in a package, or one folder above the package level, or ... ;)
Versioning and conditional compilation are the right tools for the job.


> 6b) A way to define groups of unittests (that cross modules too):  
> because you sometimes want to unittest for a feature spread in more than  
> one module, or you want to tell apart quick and slow unittests, to run  
> fast unittests often and slow ones only once in a while.
>
> One way to support unittest groups is to allow for tag names after the  
> unittest name that define overlapping groups:
>
> unittest(foo_name, group1_tag_name, group2_tag_name, ...) {...}

It's more of a version statement issue: can't have many versions or'ed  
together in a single condition. Then again, this is possible now:

template isVersion(string name) {
     enum bool isVersion = !is(typeof({
          mixin("version("~name~") {
                static assert(false);
          }");
      }));
}

static if (isVersion!"group1_tag_name" || isVersion!"group2_tag_name" ||  
... )
mixin Unittest!("foo_name", {...});


[snip]
> A) Serious unittest system needs a way to allow sharing of setup and  
> shutdown code for tests.

How about:

unittest {
     setUp();
     scope(exit) tearDown();
     ...
};


> Fixtures can be supported at the package, module, class and function  
> level. Setup always runs before any test (or groups of tests).

Unittests are executed in declaration order, so:

unittest { setUp(); }
unittest { ... }
unittest { ... }
unittest { tearDown(); }


In a nutshell, I agree with you that unittests would use more features but  
we should explore a (standard?) library solution first, before putting  
more metal into the language/compiler.


Tomek

** Actually, it doesn't compile now:

mixin Unittest!("my_test", {
     assert (2 + 2 == 4);
});

Error: delegate test.__dgliteral2 is a nested function and cannot be  
accessed from my_test

I'd appreciate if someone elaborated on why exactly it cannot be accessed  
 from my_test. To me, if a delegate literal is defined in the global scope,  
then, with respect to nesting, it's not much different than a normal  
function, no?


More information about the Digitalmars-d mailing list