原创作者: oldrev
阅读:3293次
评论:0条
更新时间:2011-05-26
一个模仿 Ruby Test::Unit 的 Quick & Dirty 单元测试框架,托 __traits 的福,看起来已经有那么点意思了。提取行号在目前还没法实现,估计等 macro 出来就能解决这个问题。
SVN里的最新版在下面的链接处:
dotmars.googlecode.com/svn/trunk/sandbox/2.0/test.d
D2.0 代码
运行结果
oldrev@ubuntu:~/work/dotmars/sandbox/2.0$ dmd2 -run test.d
Started...
Finished
Error: stest.MyTestCase.testOne
Exception raised
Failure: test.MyTestCase.testOne [test.MyTestCase]
A stupid assertion
Failure: test.MyTestCase2.testThree [test.MyTestCase2]
Yet another stupid assertion
7 tests, 9 assertions, 2 failures, 1 errors
Happy hacking!
SVN里的最新版在下面的链接处:
dotmars.googlecode.com/svn/trunk/sandbox/2.0/test.d
D2.0 代码
- /**
- A D 2.0 unit test framework inspired by Ruby's Unit::Test
- // Written in the D programming language 2.0
- Authors: Wei Li (oldrev@gmail.com)
- License: BSD
- Copyright: Copyright (C) 2007 by Wei Li.
- */
- import std.stdio;
- ////////////////////////////////////////////////////////////////////////////////
- struct Failure
- {
- string location;
- string message;
- string testName;
- }
- ////////////////////////////////////////////////////////////////////////////////
- struct Error
- {
- Exception exception;
- string testName;
- }
- ////////////////////////////////////////////////////////////////////////////////
- class TestResult
- {
- private Error[] m_errors;
- private Failure[] m_fails;
- private int m_runCount;
- private int m_assertionCount;
- private int m_testCount;
- const(Error)[] errors() {
- return m_errors;
- }
- const(Failure)[] failures() {
- return m_fails;
- }
- void addFailure(const string loc, const string msg, const string name)
- {
- Failure f;
- with(f) {
- location = loc;
- message = msg;
- testName = name;
- }
- m_fails ~= f;
- }
- void addError(Exception ex, const string name)
- {
- Error e;
- with(e) {
- exception = ex;
- testName = name;
- }
- m_errors ~= e;
- }
- void addAssertion() {
- m_assertionCount++;
- }
- void addTest() {
- m_testCount++;
- }
- void addRun() {
- m_runCount++;
- }
- bool hasPassed() {
- return m_errors.length == 0 && m_fails.length == 0;
- }
- int errorCount() {
- return cast(int)m_errors.length;
- }
- int failureCount() {
- return cast(int)m_fails.length;
- }
- int runCount() {
- return m_runCount;
- }
- int testCount() {
- return m_testCount;
- }
- int assertionCount() {
- return m_assertionCount;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- abstract class TestBase
- {
- protected this() {
- }
- abstract void run(TestResult result);
- abstract const bool isRunning();
- }
- ////////////////////////////////////////////////////////////////////////////////
- abstract class TestCase(Subclass) : TestBase
- {
- alias typeof(this) SelfType;
- struct TestMethod
- {
- string name;
- void delegate() method;
- }
- public const string name = Subclass.classinfo.name;
- private TestResult m_result;
- private TestMethod[] m_methods;
- private size_t m_currentMethod;
- private bool m_isFailed;
- private bool m_running = false;
- this() {
- }
- private static const(string) ctfMakeString(T)()
- {
- string ret;
- foreach(str; __traits(allMembers, T)) {
- if(str[0..4] == "test")
- ret ~= `addTestMethod(TestMethod("` ~ str ~ `", &sc.` ~ str ~ `)); ` ~ "\n";
- }
- return ret;
- }
- private void initial(const Subclass sc) {
- mixin(ctfMakeString!(Subclass)());
- }
- void addTestMethod(TestMethod tm) {
- m_methods ~= tm;
- }
- static Subclass createChild() {
- auto o = new Subclass;
- o.initial(o);
- return o;
- }
- void setup() {}
- void teardown() {}
- override const bool isRunning() {
- return m_running;
- }
- override void run(TestResult result)
- {
- m_result = result;
- m_result.addRun();
- foreach(size_t i, TestMethod tm; m_methods)
- {
- m_isFailed = false;
- m_currentMethod = i;
- m_result.addTest();
- setup();
- m_running = true;
- try {
- tm.method();
- }
- catch(Exception ex) {
- m_result.addError(ex, currentMethodName);
- }
- finally {
- m_running = false;
- }
- teardown();
- }
- }
- const string currentMethodName() {
- return name ~ "." ~ m_methods[m_currentMethod].name;
- }
- private void addFailure(const string message = null)
- {
- if(!m_isFailed)
- {
- m_isFailed = true;
- m_result.addFailure(name, message, currentMethodName);
- }
- }
- //////////////////////////// Assertion Functions ///////////////////////////
- void assertTrue(bool x, const string message = null)
- {
- m_result.addAssertion();
- if(!x) {
- addFailure(message);
- }
- }
- void assertNull(T)(const T value, const string message = null)
- {
- m_result.addAssertion();
- if(value !is null) {
- addFailure(message);
- }
- }
- void assertNotNull(T)(const T value, const string message = null)
- {
- m_result.addAssertion();
- if(value is null) {
- addFailure(message);
- }
- }
- void assertEqual(T)(const T expected, const T actual, const string message = null)
- {
- m_result.addAssertion();
- if(expected != actual) {
- addFailure(message);
- }
- }
- void assertNotEqual(T)(const T expected, const T actual, const T delta, const string message = null)
- {
- m_result.addAssertion();
- if(expected == actual) {
- addFailure(message);
- }
- }
- void flunk(const string message = "Flunked")
- {
- m_result.addAssertion();
- addFailure(message);
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- class TestSuit(Subclass, Tests...) : TestBase
- {
- alias typeof(this) SelfType;
- public const string name = Subclass.classinfo.name;
- private TestBase[] m_tests;
- private bool m_running = false;
- this()
- {
- m_running = false;
- foreach(T; Tests)
- {
- T test = T.createChild();
- addTest(test);
- }
- }
- static Subclass createChild() {
- return new Subclass;
- }
- const(TestBase)[] tests() {
- return m_tests;
- }
- void addTest(TestBase tb)
- in {
- assert(tb !is null);
- }
- body {
- m_tests ~= tb;
- }
- const bool empty() {
- return Tests.length == 0;
- }
- override const bool isRunning() {
- return m_running;
- }
- override void run(TestResult result) {
- m_running = true;
- foreach(test; m_tests) {
- test.run(result);
- }
- m_running = false;
- }
- }
- static class ConsoleRunner
- {
- static void showFailures(TestResult tr)
- {
- foreach(fail; tr.failures)
- {
- writefln("Failure: %s [%s]", fail.testName, fail.location);
- writefln("%s", fail.message);
- writefln();
- }
- }
- static void showErrors(TestResult tr)
- {
- foreach(err; tr.errors)
- {
- writefln("Error: s", err.testName);
- writefln("%s", err.exception.msg);
- writefln();
- }
- }
- static void run(TestBase tb)
- {
- auto result = new TestResult;
- writefln("Started...");
- tb.run(result);
- writefln("Finished\n");
- showErrors(result);
- showFailures(result);
- writefln();
- writefln("%d tests, %d assertions, %d failures, %d errors",
- result.testCount, result.assertionCount, result.failureCount, result.errorCount);
- if(result.hasPassed)
- writefln("Everything is OK.");
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- class MyTestCase : TestCase!(MyTestCase)
- {
- void testOne() {
- assertTrue(false, "A stupid assertion");
- assertTrue(true);
- assertTrue(true);
- throw new Exception("Exception raised");
- }
- void testTwo() {
- assertTrue(true);
- }
- void testThree() {
- assertTrue(true);
- }
- }
- class MyTestCase2 : TestCase!(MyTestCase2)
- {
- void testOne() {
- assertTrue(true);
- }
- void testTwo() {
- assertTrue(true);
- }
- void testThree() {
- assertTrue(false, "Yet another stupid assertion");
- }
- }
- class MyTestCase3 : TestCase!(MyTestCase3)
- {
- void testMethod() {
- assertTrue(true);
- }
- }
- class MyTestSuit1: TestSuit!(MyTestSuit1, MyTestCase)
- {
- }
- class MyTestSuit2: TestSuit!(MyTestSuit2, MyTestCase2)
- {
- }
- class MyTestSuit3: TestSuit!(MyTestSuit3, MyTestSuit1, MyTestSuit2, MyTestCase3)
- {
- }
- void main()
- {
- auto ts = new MyTestSuit3;
- ConsoleRunner.run(ts);
- }
运行结果
Started...
Finished
Error: stest.MyTestCase.testOne
Exception raised
Failure: test.MyTestCase.testOne [test.MyTestCase]
A stupid assertion
Failure: test.MyTestCase2.testThree [test.MyTestCase2]
Yet another stupid assertion
7 tests, 9 assertions, 2 failures, 1 errors
Happy hacking!
评论 共 0 条 请登录后发表评论