From: Michel Demazure Date: 2009-03-30T20:05:44+09:00 Subject: Re: testy.rb - ruby testing that's mad at the world Brian Candler wrote: > Bil Kleb wrote: >> Sounds like you're pining for Python's doctest? >> >> http://docs.python.org/library/doctest.html > > Ah, now that's a really interesting way of thinking about > examples/testing: in the form of an irb session. You can write your > tests just by mucking about in irb, and when it makes sense, just paste > the output somewhere. > > irb> foo = generate_foo > => # > irb> foo.attr1 > => "hello" > irb> foo.attr2 > => "world" > > This has been done : I found it quite some time ago on the Web and polished it a bit : #!/usr/bin/env ruby # --------------------------------------------------- # Copyright 2007 Clinton Forbes # Modified Michel Demazure 11/07 # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see http://www.gnu.org/licenses/. # --------------------------------------------------- # Put this in the script directory of your Rails app # this script is inspired by the doctest feature in Python: # (http://docs.python.org/lib/module-doctest.html) # :although I never did get around to reading the original Python code. # # Feel free to improve upon this script, on the condition that you post # a comment on http://clintonforbes.blogspot.com/2007/08/doctest-for-ruby-and-rails.html # and let me know about it so I can use it as well. # # to use, just put some doctests in your code and run this tool using # # script/doctest # doctests look like this =begin #doctest Check that 1 + 1 = 2 >> x = 1 => 1 >> x + x => 3 >> 2 + 3 => 6 =end # or like this #=begin ##doctest Test creating a Member #>> m = Member.new() #=> #nil, "name"=>nil, "hashed_password"=>nil, "admin"=>false, "reset_key"=>nil, "newsletter_level"=>nil, "created_at"=>nil, "email"=>nil}> #>> m.name = "Clinton" #=> "Clinton" #>> m.admin = true #=end # or like this =begin #doctest Check that 1 + 2 = 3 irb(main):001:0> 1 + 2 => 3 irb(main):002:0> 2 + 3 => 5 =end #get all of our Rails stuff #require File.dirname(__FILE__) + '/../config/boot' #require File.dirname(__FILE__) + '/../config/environment' #build array of .rb files #only looks in ./app directory by default. you can change this if you keep #extra code in other places (eg. ./vendor/plugins) # CODE_REGEX caters for standard IRB prompt and Rails script/console # prompt. CODE_REGEX = Regexp.new(/(>>|irb.*?>) (.*)/) RESULT_REGEX = Regexp.new(/=> (.*)/) def get_ruby_files(dir_name) ruby_file_names = [] Dir.foreach(dir_name) do |file_name| unless file_name == '.' || file_name == '..' full_name = File.join(dir_name, file_name) if /.*\.rb$/ =~ full_name ruby_file_names << full_name elsif File.directory? full_name sub_files = get_ruby_files(full_name) ruby_file_names.concat(sub_files) unless sub_files.empty? end end end ruby_file_names end # When running tests, addresses of objects are never likely # to be the same, so we wipe them out so tests don't fail # # for example: # def normalize_result(input) input.gsub(/:0x([a-f0-9]){8}/, ':0xXXXXXXXX') end def failure_report(statement, expected_result, result) report = "\n FAILED" #add line number logic here report << " Code: " << statement << "\n" report << " Expected: " << expected_result << "\n" report << " But got: " << result end def run_doc_tests(doc_test) execution_context = binding() statement, report = '', '' wrong, passed = 0, 0 doc_test.split("\n").each do |line| case line when CODE_REGEX statement << CODE_REGEX.match(line)[2] when RESULT_REGEX expected_result = normalize_result(RESULT_REGEX.match(line)[1]) result = normalize_result(eval(statement, execution_context).inspect) unless result == expected_result report << failure_report(statement, expected_result, result) wrong += 1 else passed += 1 end statement = '' end end return passed, wrong, report end def process_ruby_file(file_name) tests, succeeded, failed = 0, 0, 0 file_report = '' code = File.read(file_name) code.scan(/=begin\s#doctest ([^\n]*)\n(.*?)=end/m) do |doc_test| file_report << "\n Testing '#{doc_test[0]}'..." passed, wrong, report = run_doc_tests(doc_test[1]) file_report += (wrong == 0 ? "OK" : report) tests += 1 succeeded += passed failed += wrong end file_report = "Processing '#{file_name}'" + file_report unless file_report.empty? return tests, succeeded, failed, file_report end ruby_file_names = get_ruby_files(File.dirname(__FILE__)) total_report = "Looking for doctests in #{ruby_file_names.length} files\n" total_files, total_tests, total_succeeded, total_failed = 0, 0, 0, 0 ruby_file_names.each do |ruby_file_name| tests, succeeded, failed, report = process_ruby_file(ruby_file_name) total_files += 1 if tests > 0 total_tests += tests total_succeeded += succeeded total_failed += failed total_report << report << "\n" unless report.empty? end total_report << "Total files: #{total_files}, total tests: #{total_tests}, assertions succeeded: #{total_succeeded}, assertions failed: #{total_failed}" puts total_report -- Posted via http://www.ruby-forum.com/.