public
Description: Ruby version of Python's doctest tool, but a bit different
Home | Edit | New

Example Usage

Create a .doctest document, for example, “simple.doctest”, and begin documenting your application, like this:

Here is an example doctest file (say called simple.doctest):

This is an example test that succeeds
>> 1 + 2
=> 3

And here’s a test that will fail
>> 1 + 2
=> 4

Test a some multiline statements
>> class Person
attr_accessor :name
end
>> Person
=> Person

>> p = Person.new
>> p.name = “Tom”
>> p.name
=> “Tom”
doctest: you split a file into separate named tests by adding a doctest: directive
>> 1 + 2
=> 4

Or, put your pastes in as comments within your ruby code, say, factorial.rb, like this:

  1. doctest: factorial should give correct results for 0 to 2
  2. >> factorial(0)
  3. => 1
  4. >> factorial(1)
  5. => 1
    #
  6. comments describing the parameters can be included
  7. this is how it works with 2:
  8. >> factorial(2)
  9. => 2
    def factorial(n)
    if n == 0
    1
    else
    n * factorial(n-1)
    end
    end
    #
  10. doctest: should work for 3, too
  11. >> factorial(3)
  12. => 6

When the above code is run, i.e. with the command “rubydoctest factorial.rb”, it shows the following results:

=== Testing 'factorial.rb'...
 OK  | factorial should give correct results for 0 to 3
 OK  | should work for 3, too
6 comparisons | 2 doctests | 0 failures | 0 errors

You can do multiline statements like this, as long as the indentation level is greater than the >> start line (note that ‘end’ is at an unnatural indentation level):

		# >> all_true = true
		# >> 3.times do |t|
		#      all_true &&= (a[t] == b[t])
		#      end
		# >> all_true
		# => true

Here’s a more advanced usage example, in a Rails model, book.rb:

require File.join(File.dirname(__FILE__), "..", "rubydoctest_helper")

class Book < ActiveRecord::Base

  1. === Description
  2. Extension for sections and chapters collections
  3. e.g. book.sections.approved_only, book.chapters.dirty!
  4. === Tests
  5. doctest: Sample data should provide an example of a book with both approved
  6. and unapproved sections.
  7. >> v_a_books = Book.varied(:approved)
  8. >> v_a_books.size > 0
  9. => true
  10. doctest: Sample data should provide an example of a book with both dirty
  11. and non-dirty sections.
  12. >> v_d_books = Book.varied(:dirty)
  13. >> v_d_books.size > 0
  14. => true
    module SectionExtension
  15. === Tests
  16. doctest: Book#sections.approved_only should return approved sections,
  17. including first-level sections, i.e “chapters”.
  18. >> s = v_a_books.first.sections.approved_only
  19. >> s.size > 1
  20. => true
  21. >> s.any?{ |t| t.approved? }
  22. => true
  23. >> s.any?{ |t| !t.approved? }
  24. => false
    def approved_only
    self.select{ |s| s.approved? }
    end
  1. === Tests
  2. doctest: Book#sections.unapproved_only should return unapproved sections,
  3. including first-level sections, i.e “chapters”.
  4. >> s = v_a_books.first.sections.unapproved_only
  5. >> s.size > 1
  6. => true
  7. >> s.any?{ |t| t.approved? }
  8. => false
  9. >> s.any?{ |t| !t.approved? }
  10. => true
    def unapproved_only
    self.select{ |s| !s.approved? }
    end
  1. === Tests
  2. doctest: Book#sections.dirty! should set the dirty flag for sections
  3. specific to the particular book it was called on.
  4. >> book = v_d_books.first
  5. >> (yes = book.sections.count(:conditions => [“dirty = ?”, true])) > 0
  6. => true
  7. >> (no = book.sections.count(:conditions => [“dirty = ?”, false])) > 0
  8. => true
  9. >> v_d_books.first.sections.dirty!
  10. >> book.sections.count(:conditions => [“dirty = ?”, true])
  11. => yes + no
  12. >> book.sections.count(:conditions => [“dirty = ?”, false])
  13. => 0
    def dirty!
    self.update_all [“dirty = ?”, true]
    end
    end
has_many :sections, :extend => SectionExtension
  1. === Description
  2. Find books with a diverse set of sections with a certain property.
  3. === Examples
  4. @see SectionExtension module
    def self.varied(field, min = 2)
    find_by_sql \
    SELECT * FROM books WHERE id IN
    (SELECT book_id FROM sections
    GROUP BY book_id
    HAVING COUNT >= #{min})”
    end
    end

See the Rails Usage page for more information about the rubydoctest_helper script mentioned above.

In each case, you run your tests using the rubydoctest command-line tool, e.g.:


$ rubydoctest doc/sample.doctest
$ rubydoctest factorial.rb
$ rubydoctest app/models/book.rb

Note also that you can use =begin and =end to suround the tests, ex: factorial.rb


def factorial(n)
  if n == 0
    1
  else
    n * factorial(n-1)
  end
end
=begin
doctest: fact
>> factorial(0)
=> 1
=end

And that tests can be interspersed with comments, a la

  1. doctest: factial 0 is 1
  2. >> factorial(0)
  3. => 1
    #
  4. These numbers grow much larger with a higher n:
    #
  5. >> factorial(10)
  6. => 3628800

And since your test code and comments will appear in the rdocs, you’ve made instant documentation for parameters to your functions.

And all it cost was a copy and past from an irb session.

Last edited by canadaduane, Wed Jul 02 11:34:58 -0700 2008
Home | Edit | New
Versions: