/test/passenger_pane/NuBacon/README.md

http://github.com/tgunr/passengerpane · Markdown · 305 lines · 217 code · 88 blank · 0 comment · 0 complexity · 2dc8ddc17cf9a63b7f4adaeaeea8fed5 MD5 · raw file

  1. NuBacon -- small RSpec clone
  2. ============================
  3. "Truth will sooner come out from error than from confusion."
  4. ---Francis Bacon
  5. NuBacon is a [Nu][nu] port of [Bacon][ba], a small [Ruby RSpec][rs] clone.
  6. It is a [Behavior-Driven Development][bdd] test library for Nu and in
  7. extension for Objective-C. It is being developed while using in our iOS
  8. application, more on that will be announced.
  9. Installation
  10. ------------
  11. There's currently no Nu specific package manager, so you will have to
  12. grab the source directly:
  13. As a zip archive:
  14. $ curl https://github.com/alloy/NuBacon/zipball/0.1 -o NuBacon-0.1.zip
  15. $ unzip NuBacon-0.1.zip
  16. Or as a git clone:
  17. $ git clone git@github.com:alloy/NuBacon.git
  18. $ cd NuBacon
  19. $ git checkout 0.1
  20. Or checkout master if youre feeling adventurous. The runloop code,
  21. for instance, is not yet available in a release.
  22. Whirl-wind tour
  23. ---------------
  24. (load "bacon")
  25. (set emptyArray (do (array) (eq (array count) 0)))
  26. (describe "An array" `(
  27. (before (do ()
  28. (set @ary (NSArray array))
  29. (set @otherArray (`("noodles") array))
  30. ))
  31. (it "is empty" (do ()
  32. (~ @ary should not containObject:1)
  33. ))
  34. (it "has zero elements" (do ()
  35. (~ @ary count should be:0)
  36. (~ @ary count should not be closeTo:0.1) ; default delta of 0.00001
  37. (~ @ary count should be closeTo:0.1 delta:0.2)
  38. ))
  39. (it "raises when trying to fetch an element" (do ()
  40. (set exception (-> (@ary objectAtIndex:0) should raise:"NSRangeException"))
  41. (~ (exception reason) should match:/beyond bounds/)
  42. ))
  43. (it "compares to another object" (do ()
  44. (~ @ary should be:@ary)
  45. (~ @ary should equal:@ary)
  46. (~ @otherArray should not be:@ary)
  47. (~ @otherArray should not equal:@ary)
  48. ))
  49. (it "changes the count when adding objects" (do ()
  50. (-> (@otherArray << "soup") should change:(do () (@otherArray count)) by:+1)
  51. ))
  52. (it "performs a long running operation" (do ()
  53. (@otherArray performSelector:"addObject:" withObject:"soup" afterDelay:0.5)
  54. (wait 0.6 (do ()
  55. (~ (@otherArray count) should be:2)
  56. ))
  57. ))
  58. ; Custom assertions are trivial to do, they are blocks returning
  59. ; a boolean value. The block is defined at the top.
  60. (it "uses a custom assertion to check if the array is empty" (do ()
  61. (~ @ary should be a: emptyArray)
  62. (~ @otherArray should not be a: emptyArray)
  63. ))
  64. (it "has super powers" (do ()
  65. ; flunks when it contains no assertions
  66. ))
  67. ))
  68. ((Bacon sharedInstance) run)
  69. Now run it:
  70. $ nush readme_spec.nu
  71. An array
  72. - is empty
  73. - has zero elements
  74. - raises when trying to fetch an element
  75. - compares to another object
  76. - changes the count when adding objects
  77. - performs a long running operation
  78. - uses a custom assertion to check if the array is empty
  79. - has super powers [FAILURE]
  80. An array - has super powers: flunked [FAILURE]
  81. 8 specifications (14 requirements), 1 failures, 0 errors
  82. Implemented assertions
  83. ----------------------
  84. * should:predicateBlock
  85. * should be:object
  86. * should (be) a:object
  87. * should equal:object
  88. * should (be) closeTo:__*float | list of floats*__
  89. * should (be) closeTo:__*float | list of floats*__ delta:float
  90. * should match:regexp
  91. * should change:valueBlock
  92. * should change:valueBlock by:delta
  93. * should raise
  94. * should raise:exceptionName
  95. * should __*predicate method*__
  96. * should __*dynamic predicate message matching*__
  97. * should satisfy:message block:block
  98. Predicate methods
  99. -----------------
  100. Any method of the object being tested, that can work as a predicate,
  101. can be called on the BaconShould instance that wraps it. The result
  102. of the method call will determine wether or not the assertion passes.
  103. Any return value that evaluates to `true` will pass, likewise any
  104. value that evaluates to `false` will fail. Unless the assertion has
  105. been negated with `not`.
  106. For instance, NSString has a `isAbsolutePath` predicate method:
  107. (~ "/an/absolute/path" should isAbsolutePath)
  108. (~ "a/relative/path" should not isAbsolutePath)
  109. However, as you can see this does not always lead to proper English,
  110. therefor there are a few special rules on how these methods can be
  111. called.
  112. If the predicate method starts with is it can be omitted. The
  113. previous example can thus be rewritten as:
  114. (~ "/an/absolute/path" should be an absolutePath)
  115. (~ "a/relative/path" should not be an absolutePath)
  116. Method names in the third-person perspective can be called in the
  117. first-person perspective. For example, `respondsToSelector:` can be
  118. called by omitting the s from responds:
  119. (~ "foo" should respondToSelector:"isAbsolutePath")
  120. (~ (NSArray array) should not respondToSelector:"isAbsolutePath")
  121. before/after
  122. ------------
  123. `before` and `after` need to be defined before the first specification
  124. that should have them applied.
  125. Nested contexts
  126. ---------------
  127. You can nest contexts, which will run before/after filters of parent
  128. contexts like so:
  129. (describe "parent context" `(
  130. (describe "child context" `(
  131. ))
  132. ))
  133. Shared contexts
  134. ---------------
  135. You can define shared contexts in NuBacon like this:
  136. (shared "an empty container" `(
  137. (it "has size zero" (do ()
  138. (~ (@ary count) should be:0)
  139. ))
  140. (it "is empty" (do ()
  141. (~ @ary should be: emptyArray)
  142. ))
  143. ))
  144. (describe "A new array" `(
  145. (before (do ()
  146. (set @ary (NSArray array))
  147. )
  148. (behaves_like "an empty container")
  149. ))
  150. These contexts are not executed on their own, but can be included with
  151. behaves_like in other contexts. You can use shared contexts to
  152. structure suites with many recurring specifications.
  153. The wait macro
  154. ----------------
  155. Often in Objective-C apps, code will __not__ execute immediately, but
  156. scheduled on a runloop for later execution. Therefor a mechanism is
  157. needed that will postpone execution of some assertions for a period of
  158. time. This is where the `wait` macro comes in:
  159. (it "performs a long running operation" (do ()
  160. ; Here a method call is scheduled to be performed ~0.5 seconds in the future
  161. (@otherArray performSelector:"addObject:" withObject:"soup" afterDelay:0.5)
  162. (wait 0.6 (do ()
  163. ; This block is executed ~0.6 seconds in the future
  164. (~ (@otherArray count) should be:2)
  165. ))
  166. ))
  167. The postponed block does __not__ halt the thread, but is scheduled on
  168. the runloop as well. This means that your runloop based code will have
  169. a chance to perform its job before the assertions in the block are
  170. executed.
  171. You can schedule as many blocks as youd want and even nest them.
  172. Helper macros
  173. -------------
  174. Nesting calls to assertions can become unreadable quite fast:
  175. (((((@ary count) should) not) be) closeTo:0.1 delta:0.2)
  176. For this purpose, the `~` macro has been introduced. It iterates over
  177. the symbols in the given list and sends those as messages to the
  178. object, which is the first item in the list:
  179. (~ @ary count should not be closeTo:0.1 delta:0.2)
  180. -------------
  181. The `raise` and `raise:` assertions will execute the block, which is
  182. the wrapped object, and assert that an exception is, or isn't, raised.
  183. But creating a block and wrapping it in a BaconShould instance can
  184. look a bit arcane, and you have to remember to use `send`:
  185. ((send (do () ((NSArray array) objectAtIndex:0)) should) raise:"NSRangeException")
  186. Therefore the `->` macro has been introduced:
  187. (-> (@ary objectAtIndex:0) should raise:"NSRangeException")
  188. As you might have been able to tell, any extra messages are
  189. dynamically dispatched by the `~` macro.
  190. Thanks to
  191. ---------
  192. * [Christian Neukirchen][cn], and other contributors, for Bacon itself!
  193. * Tim Burks for Nu
  194. * Laurent Sansonetti for brainwashing me about lisps ;)
  195. Contributing
  196. ------------
  197. There's still plenty to do, see the [TODO][td] for things that need to be done.
  198. Once you've made your great commits:
  199. 1. [Fork][fk] NuBacon
  200. 2. Create a topic branch - `git checkout -b my_branch`
  201. 3. Push to your branch - `git push origin my_branch`
  202. 4. Create a pull request or [issue][is] with a link to your branch
  203. 5. That's it!
  204. LICENSE
  205. -------
  206. Copyright (C) 2010 Eloy Durán <eloy.de.enige@gmail.com>, Fingertips BV <fngtps.com>
  207. NuBacon is freely distributable under the terms of an MIT-style license.
  208. See [LICENSE][li] or http://www.opensource.org/licenses/mit-license.php.
  209. [nu]: https://github.com/timburks/nu
  210. [ba]: https://github.com/chneukirchen/bacon
  211. [rs]: http://rspec.rubyforge.org
  212. [bdd]: http://behaviour-driven.org
  213. [fk]: http://help.github.com/forking
  214. [is]: https://github.com/alloy/NuBacon/issues
  215. [li]: https://github.com/alloy/NuBacon/blob/master/LICENSE
  216. [td]: https://github.com/alloy/NuBacon/blob/master/TODO
  217. [cn]: http://chneukirchen.org