`
oldrev
  • 浏览: 229896 次
  • 性别: Icon_minigender_1
  • 来自: 昆明
社区版块
存档分类
最新评论

基于 D 2.0 编译时反射的单元测试框架

阅读更多
一个模仿 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!
分享到:
评论
3 楼 xgene 2007-07-28  
 
2 楼 oldrev 2007-07-28  
1 楼 oldrev 2007-07-27  
Ruby的文档太成问题了,Programming Ruby 也不是很清楚

相关推荐

    Reflector.zip

    对 .net1.0框架dll .net2.0框架dll反编译,利用反射机制获取.

    .NET Reflector 8.3.0.93 绿色汉化版(.NET反编译工具)

    .NET 框架向全世界引入了可用来分析任何基于 .NET 的代码(无论它是单个类还是完整的程序集)的反射概念。反射还可以用来检索有关特定程序集中包含的各种类、方法和属性的信息。使用 .NET Reflector,你可以浏览程序...

    YourName.exe.config

    .net应用程序配置文件,可以让.net2.0编译的应用程序优先用.net4.0的框架运行,从而能利用反射使用.net4.0下面的所有功能,对于.net2.0的程序的兼容性有提升

    北京中科信软 Visual Basic.NET培训

    Visual Studio Team System 中的单元测试与Web测试 案例分析:基于.NET2.0的大型电子商务系统 五 Windows 应用(Windows Forms) 创建Windows应用程序 主要Windows控件与自定义控件 WinForm中的数据处理 多...

    asp.net知识库

    ASP.NET 2.0 中的代码隐藏和编译 ASP.NET 2.0 Language Swithcer and Theme Swicher 多语言转换和多样式主题转换 ASP.NET2.0 ObjectDataSource的使用详解(1) ASP.NET2.0 ObjectDataSource的使用详解(2) ...

    LinQ入门教程.rar

    LINQ:Language Integrated Query 语言集成查询,其本质是对ADO.NET结果集通过反射连同泛型特性转换成对象集,实现OR模型的转换(类似JAVA中Hibernate框架,.NET中的NHibernate),它完全是基于.NET 2.0 的框架。...

    .Net十大必备工具之一

    NUnit是一个.NET上的单元测试框架。NUnit 1.x主要是移植JUnit 3.8。从2.0版本开始,NUnit进行了重写和重新设计,使用Attributes代替特定的方法和相应的基类。 6. MyGeneration MyGeneration是一个功能很强大的代码...

    .Net十大必备工具之二

    NUnit是一个.NET上的单元测试框架。NUnit 1.x主要是移植JUnit 3.8。从2.0版本开始,NUnit进行了重写和重新设计,使用Attributes代替特定的方法和相应的基类。 6. MyGeneration MyGeneration是一个功能很强大的代码...

    LevenBlog2.0.7版源码

    理论上可以大幅提升反射执行时间,在以前的版本中,实体赋值通过反射的后期绑定执行,效率相对来说比较低下,本次更新使用全新的基于Emit的快速反射框架,通过Emit编译委托的方式大幅加快反射速度,理论上可能有十倍以上的...

    fastCSharp

    在我的项目里,代码生成可以说是无处不在,有的时候是为了取代反射改善运行效率,有的时候是为了更方便的编写程序,有的时候是为了自动单元测试...那你将是为了什么呢? 我贡献的内容属于基础框架,不关联业务逻辑...

    leetcode回朔与贪心的题目-interview:面试官的编程

    集合框架 1.2 I/O 1.3 Socket 网络通信 1.4 注解 1.5 反射 1.6 线程基础 1.7 Java 8+( >= 11) 新特性 1,8 常见面试题剖析 2. JVM 2.0 自己编译 JDK 2.1 内存区域与OOM 2.1.1 运行时数据区域 2.1.2 2.2 垃圾收集器与...

    LevenBlog v2.0.7 源码版

    理论上可以大幅提升反射执行时间,在以前的版本中,实体赋值通过反射的后期绑定执行,效率相对来说比较低下,本次更新使用全新的基于Emit的快速反射框架,通过Emit编译委托的方式大幅加快反射速度,理论上可能有十倍以上的...

    【。net 专业】 面试题

    如果一个密封类被指定为其它类的基类,则会发生编译时错误。 密封类不能同时为抽象类。 sealed 修饰符主要用于防止非有意的派生,但是它还能促使某些运行时优化。具体说来,由于密封类永远不会有任何派生类,所以对...

    整理后java开发全套达内学习笔记(含练习)

    FrameWork [java] 结构,框架 ['freimwә:k] Generic [java] 泛型 [dʒi'nerik] goto (保留字) 跳转 heap n.堆 [hi:p] implements (关键字) 实现 ['implimәnt] import (关键字) 引入(进口,输入) Info n.信息 ...

Global site tag (gtag.js) - Google Analytics