Example Usage

kbacha edited this page Mar 23, 2013 · 23 revisions

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:

# doctest: factorial should give correct results for 0 to 2
# >> factorial(0)
# => 1
# >> factorial(1)
# => 1
#
# comments describing the parameters can be included
# this is how it works with 2:
# >> factorial(2)
# => 2
def factorial(n)
  if n == 0
    1
  else
    n * factorial(n-1)
  end
end
#
# doctest: should work for 3, too
# 
# >> factorial(3)
# => 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
  # === Description
  # Extension for sections and chapters collections
  #   e.g. book.sections.approved_only, book.chapters.dirty!
  # 
  # === Tests
  # doctest: Sample data should provide an example of a book with both approved
  #          and unapproved sections.
  # >> v_a_books = Book.varied(:approved)
  # >> v_a_books.size > 0
  # => true
  # 
  # doctest: Sample data should provide an example of a book with both dirty
  #          and non-dirty sections.
  # >> v_d_books = Book.varied(:dirty)
  # >> v_d_books.size > 0
  # => true
  module SectionExtension
    # === Tests
    # doctest: Book#sections.approved_only should return approved sections,
    #          including first-level sections, i.e "chapters".
    # >> s = v_a_books.first.sections.approved_only
    # >> s.size > 1
    # => true
    # >> s.any?{ |t| t.approved? }
    # => true
    # >> s.any?{ |t| !t.approved? }
    # => false
    def approved_only
      self.select{ |s| s.approved? }
    end

    # === Tests
    # doctest: Book#sections.unapproved_only should return unapproved sections,
    #          including first-level sections, i.e "chapters".
    # >> s = v_a_books.first.sections.unapproved_only
    # >> s.size > 1
    # => true
    # >> s.any?{ |t| t.approved? }
    # => false
    # >> s.any?{ |t| !t.approved? }
    # => true
    def unapproved_only
      self.select{ |s| !s.approved? }
    end

    # === Tests
    # doctest: Book#sections.dirty! should set the dirty flag for sections
    #          specific to the particular book it was called on.
    # >> book = v_d_books.first
    # >> (yes = book.sections.count(:conditions => ["dirty = ?", true])) > 0
    # => true
    # >> (no = book.sections.count(:conditions => ["dirty = ?", false])) > 0
    # => true
    # >> v_d_books.first.sections.dirty!
    # >> book.sections.count(:conditions => ["dirty = ?", true])
    # => yes + no
    # >> book.sections.count(:conditions => ["dirty = ?", false])
    # => 0
    def dirty!
      self.update_all ["dirty = ?", true]
    end
  end

  has_many :sections,  :extend => SectionExtension

  # === Description
  # Find books with a diverse set of sections with a certain property.
  # 
  # === Examples
  # @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(DISTINCT #{field}) >= #{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

# doctest: factial 0 is 1
# >> factorial(0)
# => 1
#
# These numbers grow much larger with a higher n:
#
# >> factorial(10)
# => 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.

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.