README.rdoc in shared_should-0.5.1 vs README.rdoc in shared_should-0.5.2
- old
+ new
@@ -1,19 +1,235 @@
-= shared_should
+= Shared Should - Share and reuse shoulds, contexts, and setups with Shoulda - easy, schmeasy.
-Description goes here.
+Shared Should allows you to easily create reusable shoulds, contexts and setups with familiar looking Shoulda syntax. Inspired by Rspec's shared example groups for context reuse, Shared Should allows sharing of contexts, shoulds,
+and setup blocks. Shared Should goes even further by allowing an initialization block and parameterization to fine-tune the usage of the shared functionality.
-== Contributing to shared_should
-
-* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
-* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
-* Fork the project
-* Start a feature/bugfix branch
-* Commit and push until you are happy with your contribution
-* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
-* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
+== Quick-Start Examples
-== Copyright
+Some quick examples to get you started using Shared Should. The domain is customers renting and purchasing textbooks - like we do at Bookrenter.com.
-Copyright (c) 2011 Michael Pearce. See LICENSE.txt for
-further details.
+=== Shared Should
+
+Sharing shoulds is easy.
+
+ context "Book" do
+ context "with an in-stock book" do
+ setup { @book = Book.new(:quantity => 1, :price => 10_00 }
+
+ ### Define a shared should
+ shared_should_be "available for checkout" { assert @book.available_for_checkout? }
+
+ context "with a rentable book" do
+ setup { @book.rentable = true }
+
+ ### Use the "available for checkout" shared_should
+ should_be "available for checkout"
+ end
+
+ context "with a purchasable book" do
+ setup { @book.purchasable = true }
+
+ ### Use the "available for checkout" shared_should in this context too
+ should_be "available for checkout"
+ end
+
+ ### ...or DRY it up by using .with or .when and an initialization block
+ should_be("available for checkout").when("rentable") { @book.rentable = true }
+ should_be("available for checkout").when("purchasable") { @book.purchasable = true }
+ end
+ end
+
+=== Shared Setup
+
+Sharing setups is easy, too.
+
+ context "Book" do
+ ### Define a shared setup
+ shared_setup_for "an in-stock book" { @book = Book.new(:quantity => 1, :price => 10_00) }
+
+ context "with an in-stock rentable book" do
+ ### Use the shared setup here
+ setup_for "an in-stock book"
+
+ ### Do some additional setup after the shared setup
+ setup { @book.rentable = true }
+
+ should "be available for checkout" { assert @book.available_for_checkout? }
+ end
+
+ context "with an in-stock purchasable book" do
+ ### Use the shared setup again
+ setup_for "an in-stock book"
+
+ setup { @book.purchasable = true }
+
+ should "be available for checkout" { assert @book.available_for_checkout? }
+ end
+ end
+
+=== Shared Context
+
+Sharing whole contexts? Schmeasy!
+
+ context "Book" do
+ context "with an in-stock book" do
+ setup { @book = Book.new(:quantity => 1, :price => 10_00) }
+
+ ### Define a shared context
+ shared_context_for "a book available for checkout" do
+ should "be in stock" { assert @book.quantity > 0 }
+ should "have a non-negative price" { assert @book.price > 0 }
+ should "be rentable or purchasable" { assert @book.rentable || @book.purchasable }
+ end
+
+ context "with a rentable book" do
+ setup { @book.rentable = true }
+
+ ### Run the shoulds inside the shared context with a rentable book
+ should_be "a book available for checkout"
+ end
+
+ context "with a purchasable book" do
+ setup { @book.purchasable = true }
+
+ ### Run the shoulds inside the shared context again with a purchasable book
+ should_be "a book available for checkout"
+ end
+
+ ### ...or DRY it up by using .with or .when and an initialization block
+ should_be("a book available for checkout").when("rentable") { @book.rentable = true }
+ should_be("a book available for checkout").when("purchasable") { @book.purchasable = true }
+ end
+ end
+
+== More Information on Syntax and Usage
+
+=== Finding Your Share
+
+Some rules:
+* When <tt>should_be</tt> or <tt>setup_for</tt> is invoked, it searches up the context hierarchy to find a matching shared definition.
+* You can redefine your shares by using the same name. These shares will only be available in in the current and descendant contexts.
+* Shares defined at the root (on your TestCase) are available in all contexts.
+* If you define a shared setup at the root level, you will need to call <tt>super</tt> if you have a setup instance method for your test.
+
+=== Initialization Block
+
+The shared invocation accepts an initialization block by chaining <tt>when</tt> or <tt>with</tt>. This block can be used to create or modify instance variables used by the shared functionality. It always executes before the shared functionality.
+
+ context "Book" do
+ setup { @book = Book.new(:quantity => 1, :price => 10_00) }
+
+ shared_should_be "available for checkout" { assert @book.available_for_checkout? }
+
+ context "with a rentable book" do
+ # when shared_should_be "available for checkout" is executed, @book will have rentable equal to true
+ should_be "available for checkout".when("rentable") { @book.rentable = true }
+ end
+
+ context "with a purchasable book" do
+ should_be "available for checkout".when("purchasable") { @book.purchasable = true }
+ end
+ end
+
+=== Parameterizing Shares
+
+Shared functions can also be parameterized using block parameters. This can be done for shared setups, shoulds, and the setups and shoulds contained within a shared context. The value passed to the declared shared function is the return value of the initialization block. The below example parameterizes a shared setup.
+
+ context "Book" do
+ shared_setup_for "an in-stock book" do |rentable|
+ @book = Book.new(:quantity => 1, :price => 10_00, :rentable => rentable, :purchasable => false)
+ end
+
+ context "with rentable book" do
+ # the return value of the block is "true" which will be passed as the block parameter "rentable"
+ setup_for("an in-stock book").with("a rentable book") { true }
+
+ should "be available for checkout" { assert @book.available_for_checkout? }
+ end
+ end
+
+Here is a parameterized shared should.
+
+ context "Book" do
+ context "with in-stock book" do
+ setup { @book = Book.new(:quantity => 1) }
+
+ shared_should_be "unavailable for checkout for price" do |price|
+ @book.price = price
+ assert_false @book.available_for_checkout?
+ end
+
+ should_be("unavailable for checkout for price").when("zero") { 0 }
+ should_be("unavailable for checkout for price").when("negative") { -1 }
+ end
+ end
+
+And a parameterized shared context.
+
+ context "Book" do
+ context "with in-stock book" do
+ setup { @book = Book.new(:quantity => 1) }
+
+ shared_context_for "a book available for checkout at price" do
+ # parameters are on the setup and shoulds, not on the context
+ setup { |price| @book.price = price }
+
+ # we could also access price in the should blocks, but we don't need it again
+ should "be in stock" { assert @book.quantity > 0 }
+ should "have a non-negative price" { assert @book.price > 0 }
+ should "be rentable or purchasable" { assert @book.rentable || @book.purchasable }
+ end
+
+ should_be("a book available for checkout at price").when("positive") { 10_00 }
+ end
+ end
+
+The shared functions also accept multiple parameters when the initialization block returns an array.
+
+ context "Book" do
+ context "with rentable book" do
+ setup { @book = Book.new(:rentable => true) }
+
+ shared_should_be "unavailable for checkout for quantity and price" do |quantity, price|
+ @book.quantity = quantity
+ @book.price = price
+ assert_false @book.available_for_checkout?
+ end
+
+ should_be("unavailable for checkout for quantity and price").when("zero quantity") { [0, 10_00] }
+ should_be("unavailable for checkout for quantity and price").when("zero price") { [1, 0] }
+ end
+ end
+
+=== Creating a Library of Shared Functionality
+
+The shared functions can also be re-usable across multiple test cases.
+
+In your test helper file:
+
+ class Test::Unit::TestCase
+ shared_setup_for "an in-stock book" do |rentable, purchasable|
+ @book = Book.new(:quantity => 1, :price => 10_00, :rentable => rentable, :purchasable => purchasable)
+ end
+ end
+
+In your test file:
+
+ class BookTest < Test::Unit::TestCase
+ context "with an in-stock book" do
+ shared_setup_for "an in-stock book".with { [true, true] }
+
+ should "be in stock" { assert @book.quantity > 0 }
+ end
+ end
+
+
+= Credits
+
+Shared Shoulda is maintained by Michael Pearce (michael.pearce__at__bookrenter__com) and is funded by Bookrenter.com[http://www.bookrenter.com]. Many of the ideas that have inspired Shared Should come
+from practical usage by the Bookrenter software development team and conversations with Bookrenter developers Andrew Wheeler and Philippe Huibonhoa.
+
+
+= Copyright
+
+Copyright (c) 2011 Michael Pearce, Bookrenter.com. See LICENSE.txt for further details.