Instance-specific unittests

H. S. Teoh hsteoh at quickfur.ath.cx
Mon Feb 13 15:12:15 PST 2012


I discovered something really cool today, and I thought I'd share it
with my fellow learners:

The unittest block is used for inserting unit tests that are executed at
runtime before main() is called. They are very useful for inserting
little tests after a piece of complex code, to make sure it actually
works as expected.

What's cool is that if you have a unittest block inside a class or
struct with compile-time parameters:

	struct S(bool B) {
		void method() { ... }
		unittest {
			/* test method() for correct operation */
		}
	}

then the unittest will be executed once *per instance* of S.  That is,
if your program uses both S!true and S!false, the unittest will run
twice, once for each instance. This ensures that the unittest tests all
variants of the code introduced by the compile-time parameter, up to
what your program actually uses.

But what if your unittest wants to test for a specific behaviour in a
specific instance of S?  You could write, for example:

	struct S(bool B) {
		void method() {
			static if (B) {
				/* Behaviour X */
			} else {
				/* Behaviour Y */
			}
		}
		unittest {
			S!true s;
			assert(/* test for behaviour X */);

			S!false t;
			assert(/* test for behaviour Y */);
		}
	}

The problem is that now the unittest will still run twice, but each time
it does exactly the same thing. Here's where another static if comes in
to rescue:

	struct S(bool B) {
		void method() {
			static if (B) {
				/* Behaviour X */
			} else {
				/* Behaviour Y */
			}
		}
		unittest {
			static if (B) {
				S!true s;
				assert(/* test for behaviour X */);
			} else {
				S!false t;
				assert(/* test for behaviour Y */);
			}
		}
	}

But we can do even better: since inside the static if, the value of B is
already known, we take advantage of the fact that we're inside the
parametrized scope of S, and so we can refer to the current instance of
S just by referring to "S":

	struct S(bool B) {
		void method() {
			static if (B) {
				/* Behaviour X */
			} else {
				/* Behaviour Y */
			}
		}
		unittest {
			static if (B) {
				S s;	// here S == S!true
				assert(/* test for behaviour X */);
			} else {
				S t;	// here S == S!false
				assert(/* test for behaviour Y */);
			}

			/* Test here for behaviour common to both
			 * variants */
		}
	}

And here you have it: a very clean and concise way to unittest different
compile-time variants of a struct/class. 


T

-- 
Ruby is essentially Perl minus Wall.


More information about the Digitalmars-d-learn mailing list