Dnia 05-05-2010 o 03:24:50 bearophile <bearophileh...@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?

Reply via email to