`
oldrev
  • 浏览: 233793 次
  • 性别: 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 也不是很清楚

相关推荐

    dagger2Example:带有 Dagger2、Espresso 2.0 和 mockito 的 Android 单元测试示例

    "带有 Dagger2、Espresso 2.0 和 mockito 的 Android 单元测试示例"说明了这个项目不仅涉及到Dagger2,还包含了 Espresso 2.0(用于UI测试)和 Mockito(用于模拟对象的单元测试框架)。因此,这个项目是一个综合性...

    microsoft jsharp runtime 2.0

    J#运行时环境(JSharp Runtime)包含了J#编译器和运行时支持,允许开发者编写、编译和执行J#代码。它还提供了对Java虚拟机(JVM)的部分兼容,但请注意,J#并不是完全与Java兼容的语言,因为它不支持一些Java的高级...

    C#和.NET 2.0 实战、平台、语言与框架(英文版)

    《C#和.NET 2.0 实战:平台、语言与框架》这本书由Patrick Smacchia撰写,由Paradoxal Press出版,详细介绍了C#语言和.NET 2.0平台的关键概念和技术细节。本书分为三个主要部分:第一部分介绍.NET 2.0平台,第二部分...

    Objective-C 2.0 运行时系统编程指南

    Objective-C 2.0 运行时系统是Apple开发的面向对象编程语言Objective-C的核心组成部分,它是Objective-C程序在执行时动态操作的基础。这个编程指南深入解析了Objective-C运行时系统的内部工作原理,帮助开发者更好地...

    BeanShell2.0b5源码

    - **自动化测试:** 在自动化测试框架中,BeanShell 可用于生成和执行测试脚本。 - **配置和脚本化任务:** 在系统管理和运维中,它可以用来处理配置文件,执行定期任务等。 5. **源码学习价值** - **动态语言...

    .net2.0面向对象编程揭秘

    .NET 2.0支持两种多态形式:编译时多态(方法重载)和运行时多态(方法重写)。接口(Interface)和抽象类(Abstract Class)也是实现多态的重要工具。 书中的示例源码涵盖了这些关键概念的实际应用,包括: - 第...

    Reflector.zip

    反射在许多场景下都有应用,比如插件架构、配置驱动编程、单元测试等,它为.NET开发者提供了极大的灵活性。 通过Reflector.exe,开发者可以: 1. 查看类、接口、结构、枚举等类型的定义。 2. 分析方法体,了解其...

    .NET Framework2.0 试题

    "MCP 70-536 .NET Framework2.0.pdf"可能是一份关于.NET Framework 2.0的认证考试复习资料,它可能涵盖了以上提到的所有知识点,以及更多关于.NET Framework 2.0的深入内容,如编程模型、XML Web服务、反射、调试和...

    NET 2.0 泛型编程

    在.NET 2.0中,泛型是一个强大的特性,它允许开发者在编译时实现类型安全,同时不会牺牲性能或增加代码体积。简单来说,泛型是一种在类、接口、方法等中使用类型参数的技术。这些类型参数可以在使用时指定具体类型,...

    ShareSourceCLI2.0

    "ShareSourceCLI2.0" 是一个开源项目,主要基于 C# 编写,与 .NET CLI (Common Language Infrastructure) 相关。CLI 是一种跨平台的编程环境,由ECMA-335标准定义,它为多种编程语言提供了一个统一的运行时环境,即...

    ASP.NET2.0数据库入门经典

    ASP.NET 2.0 是微软在.NET Framework 2.0 上构建的Web应用程序开发框架,它显著提升了开发效率,减少了编写代码的行数,使开发者能更快速地构建动态网站。在 ASP.NET 2.0 中,针对数据操作进行了重大改进,特别是对...

    .NET轻量级ORM--Dapper,.NET 2.0版(源码未修改版)

    - **性能优异**:Dapper避免了大量ORM框架中的额外开销,如动态代理和运行时反射,从而提供了接近原生数据库访问的性能。 - **类型安全**:通过使用`SqlMapper`类,Dapper可以确保参数和返回值与C#类型之间的类型...

    网上.net 2.0学习资源

    .NET 2.0是微软开发的一个重要框架,它为开发者提供了构建各种类型的应用程序的工具和平台。这个框架包括了编程语言(如C#、VB.NET等)、ASP.NET(用于Web应用程序开发)、WinForms(用于桌面应用程序开发)等多个...

    Expert .NET 2.0 IL Assembler

    元数据是.NET框架实现反射的基础,通过元数据,可以在运行时动态地获取和操作类型信息。 4. **编译与反编译**:了解IL可以帮助开发者使用反编译工具(如ILDASM或JetBrains dotPeek)查看编译后的.NET程序,这在调试...

    Dagger2.0使用

    在Dagger2中,它通过编译时处理而非运行时反射来实现依赖关系的管理,从而提供更好的性能和安全性。 ### 1. Dagger2的基本概念 - **Component(组件)**: 是Dagger2中的核心概念,它定义了依赖注入的接口,包含了...

    泛型教程2.0

    在C#中,泛型允许开发者定义可以使用任意类型的类、接口或方法,只需在定义时指定一个类型参数,如`<T>`,这样就可以在同一个代码框架下处理不同类型的对象,极大地增强了代码的通用性和效率。 #### 泛型机制详解 ...

    OpenGL ES 2.0编程指南

    - **代码框架**:包括初始化OpenGL ES环境、加载和编译着色器、设置顶点数据等步骤。 - **着色器加载与编译**:使用OpenGL ES 2.0提供的API来加载和编译着色器源代码。 - **渲染循环**:设置渲染状态,调用绘制命令...

    精通.NET 2.0 C# Windows窗体和自定义控件一书的源代码

    9. **Chapter10**:可能涉及到高级话题,如反射、委托和事件、设计模式等,这些都是.NET框架中强大的工具和技术,有助于提高代码的灵活性和可维护性。 通过学习这本书的源代码,开发者不仅可以巩固理论知识,还能...

    NET框架概述.pdf

    .NET框架是微软开发的一个软件开发平台,主要用于构建和运行基于Windows的应用程序。它包含了Common Language Runtime(CLR)和丰富的类库,提供了从数据库交互到网络通信等多种功能。本章将概述.NET框架的主要组件...

Global site tag (gtag.js) - Google Analytics