From: Rob Biedenharn Date: 2009-02-05T12:28:52+09:00 Subject: Re: custom assertion message for unit tests On Feb 4, 2009, at 8:39 PM, Will Parsons wrote: > Rob Biedenharn wrote: >> On Feb 4, 2009, at 5:59 PM, Will Parsons wrote: >> >>> I'm having trouble trying to figure out how to customize an >>> assertion >>> message when running unit tests. In particular, I want to check the >>> permissions on a file, so I have something like: >>> >>> assert_equal(0755, File.stat(test_file).mode & 0777) >>> >>> If it fails, I get a message like: >>> >>> <493> expected but was >>> <420>. >>> >>> and of course I would like the expected and actual permissions to be >>> displayed in octal rather than decimal. How can I do that? >> >> Perhaps something like: ... >> >> 1) Failure: >> test_bad_file_mode(SomeTest) [-:9]: >> Mode of some_file. >> <"0755"> expected but was >> <"0644">. >> >> 2 tests, 2 assertions, 1 failures, 0 errors > > This is certainly an improvement on my version, but it is not > completely > satisfying - the reason being that by explicitly converting the > permissions > to a string they get displayed as strings, i.e., I would like to see: > > <755> expected > > rather than: > > <"0755"> expected > >> Note that I'm doing the conversion to octal myself with the '%#o' and >> String#% >> >> It would be better to pull this into a helper method: >> >> $ ruby -r'test/unit' >> class SomeTest < Test::Unit::TestCase >> def assert_mode(expected_mode, actual_mode, message=nil) >> assert_equal '%#o'%expected_mode, '%#o'%actual_mode, message >> end >> >> def setup >> @test_file = 'some_file' >> File.open(@test_file, 'w') {|f| f.write "Hello!"} >> end >> def test_bad_file_mode >> File.chmod(0644, @test_file) >> >> assert_mode 0755, File.stat(@test_file).mode & 0777, "Mode of >> #{@test_file}" >> end >> >> def test_good_file_mode >> File.chmod(0755, @test_file) >> >> assert_mode 0755, File.stat(@test_file).mode & 0777, "Mode of >> #{@test_file}" >> end >> end >> __END__ >> Loaded suite - >> Started >> F. >> Finished in 0.0107 seconds. >> >> 1) Failure: >> test_bad_file_mode(SomeTest) >> [-:3:in `assert_mode' >> -:13:in `test_bad_file_mode']: >> Mode of some_file. >> <"0755"> expected but was >> <"0644">. >> >> 2 tests, 2 assertions, 1 failures, 0 errors > > I'm not sure I understand this - what's the advantage of defining the > assert_mode method over using the already defined methods? Look at the resulting test and it's failure message: assert_mode 0755, File.stat(@test_file).mode & 0777 <"0755"> expected but was <"0644">. Compared to what you had to start: assert_equal(0755, File.stat(test_file).mode & 0777) <493> expected but was <420>. Pretty close, no? >> Do you really want >> assert File.stat(@test_file).executable? >> instead of a specific mode? > > Yes, because I want also to check for being read-only in addition to > being > executable in the file permissions. > > -- > Will But you could go all the way with: $ cat ml_mode_test.rb require 'test/unit' module Test module Unit module Assertions ## # Asserts that the octal modes are equal. # If given a +:mask+ option, will only compare those bits. # # Example: # assert_mode 0444, File.stat(name).mode, "Always readable", :mask => 0777 # def assert_mode(expected_mode, actual_mode, options_or_message=nil, options=nil) if options.nil? && Hash === options_or_message message, options = nil, options_or_message elsif message = options_or_message end full_message = message ? message.chomp+"\n" : '' full_message << "<%#o> expected but was\n<%#o>"% [expected_mode, actual_mode] if Hash === options && options.has_key?(:mask) mask = options[:mask] expected_mode &= mask actual_mode &= mask full_message << " (applying %#o mask)"%mask end assert_block(full_message) { expected_mode == actual_mode } end end end end class SomeTest < Test::Unit::TestCase def setup @test_file = 'some_file' File.open(@test_file, 'w') {|f| f.write "Hello!"} File.chmod(0644, @test_file) end def test_with_equal assert_equal 0755, File.stat(@test_file).mode & 0777 end def test_with_equal_and_message assert_equal 0755, File.stat(@test_file).mode & 0777, "equal?" end def test_with_mode assert_mode 0755, File.stat(@test_file).mode & 0777 end def test_with_mode_and_mask assert_mode 0755, File.stat(@test_file).mode, :mask => 0777 end def test_with_mode_and_message assert_mode 0755, File.stat(@test_file).mode & 0777, "executable?" end def test_with_mode_big_finish assert_mode 0755, File.stat(@test_file).mode, "executable?", :mask => 0777 end end __END__ rab:code/ruby $ ruby ml_mode_test.rb Loaded suite ml_mode_test Started FFFFFF Finished in 0.074508 seconds. 1) Failure: test_with_equal(SomeTest) [ml_mode_test.rb:42]: <493> expected but was <420>. 2) Failure: test_with_equal_and_message(SomeTest) [ml_mode_test.rb:46]: equal?. <493> expected but was <420>. 3) Failure: test_with_mode(SomeTest) [ml_mode_test.rb:27:in `assert_mode' ml_mode_test.rb:50:in `test_with_mode']: <0755> expected but was <0644> 4) Failure: test_with_mode_and_mask(SomeTest) [ml_mode_test.rb:27:in `assert_mode' ml_mode_test.rb:54:in `test_with_mode_and_mask']: <0755> expected but was <0100644> (applying 0777 mask) 5) Failure: test_with_mode_and_message(SomeTest) [ml_mode_test.rb:27:in `assert_mode' ml_mode_test.rb:58:in `test_with_mode_and_message']: executable? <0755> expected but was <0644> 6) Failure: test_with_mode_big_finish(SomeTest) [ml_mode_test.rb:27:in `assert_mode' ml_mode_test.rb:62:in `test_with_mode_big_finish']: executable? <0755> expected but was <0100644> (applying 0777 mask) 6 tests, 6 assertions, 6 failures, 0 errors It just depends on how far you want to take it. -Rob Rob Biedenharn http://agileconsultingllc.com Rob@AgileConsultingLLC.com