原创作者: 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 代码

  1. /**
  2. A D 2.0 unit test framework inspired by Ruby's Unit::Test
  3. // Written in the D programming language 2.0
  4. Authors: Wei Li (oldrev@gmail.com)
  5. License: BSD
  6. Copyright: Copyright (C) 2007 by Wei Li.
  7. */
  8. import std.stdio;
  9. ////////////////////////////////////////////////////////////////////////////////
  10. struct Failure
  11. {
  12. string location;
  13. string message;
  14. string testName;
  15. }
  16. ////////////////////////////////////////////////////////////////////////////////
  17. struct Error
  18. {
  19. Exception exception;
  20. string testName;
  21. }
  22. ////////////////////////////////////////////////////////////////////////////////
  23. class TestResult
  24. {
  25. private Error[] m_errors;
  26. private Failure[] m_fails;
  27. private int m_runCount;
  28. private int m_assertionCount;
  29. private int m_testCount;
  30. const(Error)[] errors() {
  31. return m_errors;
  32. }
  33. const(Failure)[] failures() {
  34. return m_fails;
  35. }
  36. void addFailure(const string loc, const string msg, const string name)
  37. {
  38. Failure f;
  39. with(f) {
  40. location = loc;
  41. message = msg;
  42. testName = name;
  43. }
  44. m_fails ~= f;
  45. }
  46. void addError(Exception ex, const string name)
  47. {
  48. Error e;
  49. with(e) {
  50. exception = ex;
  51. testName = name;
  52. }
  53. m_errors ~= e;
  54. }
  55. void addAssertion() {
  56. m_assertionCount++;
  57. }
  58. void addTest() {
  59. m_testCount++;
  60. }
  61. void addRun() {
  62. m_runCount++;
  63. }
  64. bool hasPassed() {
  65. return m_errors.length == 0 && m_fails.length == 0;
  66. }
  67. int errorCount() {
  68. return cast(int)m_errors.length;
  69. }
  70. int failureCount() {
  71. return cast(int)m_fails.length;
  72. }
  73. int runCount() {
  74. return m_runCount;
  75. }
  76. int testCount() {
  77. return m_testCount;
  78. }
  79. int assertionCount() {
  80. return m_assertionCount;
  81. }
  82. }
  83. ////////////////////////////////////////////////////////////////////////////////
  84. abstract class TestBase
  85. {
  86. protected this() {
  87. }
  88. abstract void run(TestResult result);
  89. abstract const bool isRunning();
  90. }
  91. ////////////////////////////////////////////////////////////////////////////////
  92. abstract class TestCase(Subclass) : TestBase
  93. {
  94. alias typeof(this) SelfType;
  95. struct TestMethod
  96. {
  97. string name;
  98. void delegate() method;
  99. }
  100. public const string name = Subclass.classinfo.name;
  101. private TestResult m_result;
  102. private TestMethod[] m_methods;
  103. private size_t m_currentMethod;
  104. private bool m_isFailed;
  105. private bool m_running = false;
  106. this() {
  107. }
  108. private static const(string) ctfMakeString(T)()
  109. {
  110. string ret;
  111. foreach(str; __traits(allMembers, T)) {
  112. if(str[0..4] == "test")
  113. ret ~= `addTestMethod(TestMethod("` ~ str ~ `", &sc.` ~ str ~ `)); ` ~ "\n";
  114. }
  115. return ret;
  116. }
  117. private void initial(const Subclass sc) {
  118. mixin(ctfMakeString!(Subclass)());
  119. }
  120. void addTestMethod(TestMethod tm) {
  121. m_methods ~= tm;
  122. }
  123. static Subclass createChild() {
  124. auto o = new Subclass;
  125. o.initial(o);
  126. return o;
  127. }
  128. void setup() {}
  129. void teardown() {}
  130. override const bool isRunning() {
  131. return m_running;
  132. }
  133. override void run(TestResult result)
  134. {
  135. m_result = result;
  136. m_result.addRun();
  137. foreach(size_t i, TestMethod tm; m_methods)
  138. {
  139. m_isFailed = false;
  140. m_currentMethod = i;
  141. m_result.addTest();
  142. setup();
  143. m_running = true;
  144. try {
  145. tm.method();
  146. }
  147. catch(Exception ex) {
  148. m_result.addError(ex, currentMethodName);
  149. }
  150. finally {
  151. m_running = false;
  152. }
  153. teardown();
  154. }
  155. }
  156. const string currentMethodName() {
  157. return name ~ "." ~ m_methods[m_currentMethod].name;
  158. }
  159. private void addFailure(const string message = null)
  160. {
  161. if(!m_isFailed)
  162. {
  163. m_isFailed = true;
  164. m_result.addFailure(name, message, currentMethodName);
  165. }
  166. }
  167. //////////////////////////// Assertion Functions ///////////////////////////
  168. void assertTrue(bool x, const string message = null)
  169. {
  170. m_result.addAssertion();
  171. if(!x) {
  172. addFailure(message);
  173. }
  174. }
  175. void assertNull(T)(const T value, const string message = null)
  176. {
  177. m_result.addAssertion();
  178. if(value !is null) {
  179. addFailure(message);
  180. }
  181. }
  182. void assertNotNull(T)(const T value, const string message = null)
  183. {
  184. m_result.addAssertion();
  185. if(value is null) {
  186. addFailure(message);
  187. }
  188. }
  189. void assertEqual(T)(const T expected, const T actual, const string message = null)
  190. {
  191. m_result.addAssertion();
  192. if(expected != actual) {
  193. addFailure(message);
  194. }
  195. }
  196. void assertNotEqual(T)(const T expected, const T actual, const T delta, const string message = null)
  197. {
  198. m_result.addAssertion();
  199. if(expected == actual) {
  200. addFailure(message);
  201. }
  202. }
  203. void flunk(const string message = "Flunked")
  204. {
  205. m_result.addAssertion();
  206. addFailure(message);
  207. }
  208. }
  209. ////////////////////////////////////////////////////////////////////////////////
  210. class TestSuit(Subclass, Tests...) : TestBase
  211. {
  212. alias typeof(this) SelfType;
  213. public const string name = Subclass.classinfo.name;
  214. private TestBase[] m_tests;
  215. private bool m_running = false;
  216. this()
  217. {
  218. m_running = false;
  219. foreach(T; Tests)
  220. {
  221. T test = T.createChild();
  222. addTest(test);
  223. }
  224. }
  225. static Subclass createChild() {
  226. return new Subclass;
  227. }
  228. const(TestBase)[] tests() {
  229. return m_tests;
  230. }
  231. void addTest(TestBase tb)
  232. in {
  233. assert(tb !is null);
  234. }
  235. body {
  236. m_tests ~= tb;
  237. }
  238. const bool empty() {
  239. return Tests.length == 0;
  240. }
  241. override const bool isRunning() {
  242. return m_running;
  243. }
  244. override void run(TestResult result) {
  245. m_running = true;
  246. foreach(test; m_tests) {
  247. test.run(result);
  248. }
  249. m_running = false;
  250. }
  251. }
  252. static class ConsoleRunner
  253. {
  254. static void showFailures(TestResult tr)
  255. {
  256. foreach(fail; tr.failures)
  257. {
  258. writefln("Failure: %s [%s]", fail.testName, fail.location);
  259. writefln("%s", fail.message);
  260. writefln();
  261. }
  262. }
  263. static void showErrors(TestResult tr)
  264. {
  265. foreach(err; tr.errors)
  266. {
  267. writefln("Error: s", err.testName);
  268. writefln("%s", err.exception.msg);
  269. writefln();
  270. }
  271. }
  272. static void run(TestBase tb)
  273. {
  274. auto result = new TestResult;
  275. writefln("Started...");
  276. tb.run(result);
  277. writefln("Finished\n");
  278. showErrors(result);
  279. showFailures(result);
  280. writefln();
  281. writefln("%d tests, %d assertions, %d failures, %d errors",
  282. result.testCount, result.assertionCount, result.failureCount, result.errorCount);
  283. if(result.hasPassed)
  284. writefln("Everything is OK.");
  285. }
  286. }
  287. ////////////////////////////////////////////////////////////////////////////////
  288. class MyTestCase : TestCase!(MyTestCase)
  289. {
  290. void testOne() {
  291. assertTrue(false, "A stupid assertion");
  292. assertTrue(true);
  293. assertTrue(true);
  294. throw new Exception("Exception raised");
  295. }
  296. void testTwo() {
  297. assertTrue(true);
  298. }
  299. void testThree() {
  300. assertTrue(true);
  301. }
  302. }
  303. class MyTestCase2 : TestCase!(MyTestCase2)
  304. {
  305. void testOne() {
  306. assertTrue(true);
  307. }
  308. void testTwo() {
  309. assertTrue(true);
  310. }
  311. void testThree() {
  312. assertTrue(false, "Yet another stupid assertion");
  313. }
  314. }
  315. class MyTestCase3 : TestCase!(MyTestCase3)
  316. {
  317. void testMethod() {
  318. assertTrue(true);
  319. }
  320. }
  321. class MyTestSuit1: TestSuit!(MyTestSuit1, MyTestCase)
  322. {
  323. }
  324. class MyTestSuit2: TestSuit!(MyTestSuit2, MyTestCase2)
  325. {
  326. }
  327. class MyTestSuit3: TestSuit!(MyTestSuit3, MyTestSuit1, MyTestSuit2, MyTestCase3)
  328. {
  329. }
  330. void main()
  331. {
  332. auto ts = new MyTestSuit3;
  333. ConsoleRunner.run(ts);
  334. }



运行结果
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!
评论 共 0 条 请登录后发表评论

发表评论

您还没有登录,请您登录后再发表评论

文章信息

  • qiezi在2007-07-27创建
  • qiezi在2011-05-26更新
Global site tag (gtag.js) - Google Analytics