# MinitestLog [![Gem Version](https://badge.fury.io/rb/minitest_log.svg)](https://badge.fury.io/rb/minitest_log) ```MinitestLog``` gives you three things: - **Nested sections:** Use nested sections to structure your test (and, importantly, its log), so that the test can "tell its story" clearly. - **Data explication:** Use data-logging methods to log objects. Most collections (```Array```, ```Hash```, etc.) are automatically logged in detail. - **(And of course) Verdicts:** Use verdict methods to express assertions. Details for the verdict are logged, whether passed or failed. ## Installation ```sh gem install minitest_log ``` ## Contents - [Logs and Sections](#logs-and-sections) - [Nested Sections](#nested-sections) - [Text](#text) - [Formatted Text](#formatted-text) - [Attributes](#attributes) - [Timestamps and Durations](#timestamps-and-durations) - [Rescue](#rescue) - [Unrescued Exception](#unrescued-exception) - [Potpourri](#potpourri) - [Custom Elements](#custom-elements) - [Options](#options) - [Root Name](#root-name) - [XML Indentation](#xml-indentation) - [Summary](#summary) - [Error Verdict](#error-verdict) - [Backtrace Filter](#backtrace-filter) - [Data](#data) - [Strings](#strings) - [Hash-Like Objects](#hash-like-objects) - [Array-Like Objects](#array-like-objects) - [Other Objects](#other-objects) - [Verdicts](#verdicts) - [Assert Verdicts](#assert-verdicts) - [verdict_assert?](#verdict_assert) - [verdict_assert_empty?](#verdict_assert_empty) - [verdict_assert_equal?](#verdict_assert_equal) - [verdict_assert_in_delta?](#verdict_assert_in_delta) - [verdict_assert_in_epsilon?](#verdict_assert_in_epsilon) - [verdict_assert_includes?](#verdict_assert_includes) - [verdict_assert_instance_of?](#verdict_assert_instance_of) - [verdict_assert_kind_of?](#verdict_assert_kind_of) - [verdict_assert_match?](#verdict_assert_match) - [verdict_assert_nil?](#verdict_assert_nil) - [verdict_assert_operator?](#verdict_assert_operator) - [verdict_assert_output?](#verdict_assert_output) - [verdict_assert_predicate?](#verdict_assert_predicate) - [verdict_assert_raises?](#verdict_assert_raises) - [verdict_assert_respond_to?](#verdict_assert_respond_to) - [verdict_assert_same?](#verdict_assert_same) - [verdict_assert_silent?](#verdict_assert_silent) - [verdict_assert_throws?](#verdict_assert_throws) - [Refute Verdicts](#refute-verdicts) - [verdict_refute?](#verdict_refute) - [verdict_refute_empty?](#verdict_refute_empty) - [verdict_refute_equal?](#verdict_refute_equal) - [verdict_refute_in_delta?](#verdict_refute_in_delta) - [verdict_refute_in_epsilon?](#verdict_refute_in_epsilon) - [verdict_refute_includes?](#verdict_refute_includes) - [verdict_refute_instance_of?](#verdict_refute_instance_of) - [verdict_refute_kind_of?](#verdict_refute_kind_of) - [verdict_refute_match?](#verdict_refute_match) - [verdict_refute_nil?](#verdict_refute_nil) - [verdict_refute_operator?](#verdict_refute_operator) - [verdict_refute_predicate?](#verdict_refute_predicate) - [verdict_refute_respond_to?](#verdict_refute_respond_to) - [verdict_refute_same?](#verdict_refute_same) - [Tips](#tips) - [Use Short Verdict Aliases](#use-short-verdict-aliases) - [Avoid Failure Clutter](#avoid-failure-clutter) - [Facilitate Post-Processing](#facilitate-post-processing) - [Oh, and Tests](#oh-and-tests) - [About This README](#about-this-readme) ## Logs and Sections ### Nested Sections Use nested sections to give structure to your test -- and its log. In calling method ```section```, the first argument is the section name. Any following string arguments become text. ```example.rb```: ```ruby require 'minitest_log' class Example < MiniTest::Test def test_example MinitestLog.new('log.xml') do |log| log.section('My nested sections', 'Sections can nest.') do log.section('Outer', 'Outer section.') do log.section('Mid', 'Mid-level section') do log.section('Inner', 'Inner section.') log.section('Another','Another inner section.') end end end end end end ``` ```log.xml```: ```xml Sections can nest. Outer section. Mid-level section Inner section. Another inner section. ``` ### Text Put text onto a section by calling method ```section``` with string arguments. As before, the first argument is the section name; other string arguments become text. Multiple string arguments are concatenated into the text. Note that you can also put text onto a section by calling method ```put_data```. See [Data](#data) below. ```example.rb```: ```ruby require 'minitest_log' class Example < MiniTest::Test def test_example MinitestLog.new('log.xml') do |log| log.section('My section', 'Text for my section.') do log.section('Another section', 'Text for another section.', ' More text.') end end end end ``` ```log.xml```: ```xml Text for my section. Text for another section. More text. ``` ### Formatted Text Put formatted text onto a section by calling method ```put_pre```. Whitespace, including newlines, is preserved. The formatted text in the log is more readable if it begins and ends with suitable whitespace, so by default the method displays the text with enhanced whitespace if needed. You can specify ```verbatim = true``` to suppress the enhancement. ```example.rb```: ```ruby require 'minitest_log' class Example < MiniTest::Test def test_example MinitestLog.new('log.xml') do |log| log.section('Line of text with leading and trailing whitespace') do log.put_pre(' Text. ') end text = < ``` ### Attributes Put attributes onto a section by calling ```section``` with hash arguments. Each name/value pair in a hash becomes an attribute in the log section element. The first argument is always the section name. Following hash arguments become attributes. ```example.rb```: ```ruby require 'minitest_log' class Example < MiniTest::Test def test_example MinitestLog.new('log.xml') do |log| attrs = {:first_attr => 'first', :second_attr => 'second'} log.section('My section with attributes', attrs, 'A Hash becomes attributes.') more_attrs = {:third_attr => 'third'} log.section('My section with more attributes', attrs, more_attrs, 'More attributes.') end end end ``` ```log.xml```: ```xml A Hash becomes attributes. More attributes. ``` ### Timestamps and Durations Use symbol ```:timestamp``` or ```:duration``` to add a timestamp or a duration to a section. ```example.rb```: ```ruby require 'minitest_log' class Example < MiniTest::Test def test_example MinitestLog.new('log.xml') do |log| log.section('My section with timestamp', :timestamp, 'Section with timestamp.') log.section('My section with duration', :duration, 'Section with duration.') do sleep(0.5) end log.section('My section with both', :duration, :timestamp, 'Section with both.') do sleep(0.5) end end end end ``` ```log.xml```: ```xml Section with timestamp. Section with duration. Section with both. ``` ### Rescue Rescue a section using the symbol ```:rescue```. Any exception raised during that section's execution will be rescued and logged. Such an exception terminates the *section* (but not the *test*). ```example.rb```: ```ruby require 'minitest_log' class Example < MiniTest::Test def test_example MinitestLog.new('log.xml') do |log| log.section('My rescued section', :rescue) do raise RuntimeError.new('Boo!') log.comment('This code will not be reached, because the section terminates.') end log.comment('This code will be reached, because it is not in the terminated section.') end end end ``` ```log.xml```: ```xml This code will be reached, because it is not in the terminated section. ``` ### Unrescued Exception An exception raised in an unrescued section is logged, and the test terminates. ```example.rb```: ```ruby require 'minitest_log' class Example < MiniTest::Test def test_example MinitestLog.new('log.xml') do |log| log.section('My unrescued section') do raise RuntimeError.new('Boo!') end log.comment('This code will not be reached, because the test terminated.') end end end ``` ```log.xml```: ```xml Boo! ``` ### Potpourri So far, examples for method ```section``` have emphasized one thing at a time. A call to method ```section``` begins always with the section name, but after that it can have any number and any types of arguments. Note that: - Multiple string arguments are concatenated, left to right, to form one string in the log. - Each hash argument's name/value pairs are used to form attributes in the log. - Symbols ```:timestamp```, ```:duration```, and ```:rescue``` may appear anywhere among the arguments. Duplicates are ignored. ```example.rb```: ```ruby require 'minitest_log' class Test < Minitest::Test def test_demo MinitestLog.new('log.xml') do |log| log.section( 'Section with potpourri of arguments', # Not that you would ever want to do this. :-) :duration, 'Word', {:a => 0, :b => 1}, :timestamp, ' More words', {:c => 2, :d => 3}, :rescue, ) do sleep(0.5) raise Exception.new('Boo!') end end end end ``` ```log.xml```: ```xml Word More words ``` ### Custom Elements Use method ```put_element(name, *args)``` to put custom elements onto the log. The name must be text that can become a legal XML element name. Method ```section``` just calls ```put_element``` with the name ```section```, so the remaining allowed ```*args``` are the same for both : - A string becomes text. - A hash-like object becomes attributes. - An array-like object becomes a numbered list. A call to ```put_element``` with no block generates a childless element -- one with no sub-elements (though it may still have text). Create sub-elements for a custom element by calling ```put_element``` with a block. Whatever elements are generated by the block become sub-elements of the custom element. ```example.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_example MinitestLog.new('log.xml') do |log| log.section('Custom elements with no children') do log.put_element('childless_element') log.put_element('childless_element_with_text', 'Text for this element.') log.put_element('childless_element_with_attributes', {:a => 0, :b => 1}) end log.section('Custom elements with children') do log.put_element('parent_element') do log.put_element('child_element') end log.put_element('parent_element_with_text', 'Text for this element.') do log.put_element('child_element') end log.put_element('parent_element_with_attributes', {:a => 0, :b => 1}) do log.put_element('child_element') end end end end end ``` ```log.xml```: ```xml Text for this element. Text for this element. ``` ### Options #### Root Name The default name for the XML root element is ```log```. Override that name by specifying option ```:root_name```. ```root_name.rb```: ```ruby require 'minitest_log' class Test < Minitest::Test def test_demo MinitestLog.new('default_root_name.xml') do |log| log.section('This log has the default root name.') end MinitestLog.new('custom_root_name.xml', :root_name => 'my_root_name') do |log| log.section('This log has a custom root name.') end end end ``` ```default_root_name.xml```: ```xml ``` ```custom_root_name.xml```: ```xml ``` #### XML Indentation The default XML indentation is 2 spaces. Override that value by specifying option ```xml_indentation```. An indentation value of ```0``` puts each line of the log at the left, with no indentation. An indentation value of ```-1``` puts the entire log on one line, with no whitespace at all. This format is best for logs that will be parsed in post-processing. Note that many applications that display XML will ignore the indentation altogether, so in general it matters only when the raw XML is read (by a person) or parsed. ```xml_indentation.rb```: ```ruby require 'minitest_log' class Test < Minitest::Test def test_demo MinitestLog.new('default_xml_indentation.xml') do |log| log.section('This log has the default XML indentation (2 spaces).') do log.section('See?') end end MinitestLog.new('xml_indentation_0.xml', :xml_indentation => 0) do |log| log.section('This log has an XML indentation of 0 (no indentation).') do log.section('See?') end end MinitestLog.new('xml_indentation_-1.xml', :xml_indentation => -1) do |log| log.section('This log has an XML indentation of -1 (all on one line).') do log.section('See?') end end end end ``` ```default_xml_indentation.xml```: ```xml ``` ```xml_indentation_0.xml```: ```xml ``` ```xml_indentation_-1.xml```: ```xml ``` #### Summary By default, the log does not have a summary: counts of total verdicts, failed verdicts, and errors. Override that behavior by specifying option ```:summary``` as ```true```. This causes the log to begin with a summary. ```summary.rb```: ```ruby require 'minitest_log' class Test < Minitest::Test def test_demo MinitestLog.new('no_summary.xml') do |log| log.section('This log has no summary.') do populate_the_log(log) end end MinitestLog.new('summary.xml', :summary => true) do |log| log.section('This log has a summary.') do populate_the_log(log) end end end def populate_the_log(log) log.verdict_assert?(:pass, true) log.verdict_assert?(:fail, false) log.section('My error-producing section', :rescue) do raise Exception.new('Boo!') end end end ``` ```no_summary.xml```: ```xml ``` ```summary.xml```: ```xml ``` #### Error Verdict By default, the log does not have an error verdict: a generated verdict that expects the error count to be 0. Override that behavior by specifying option ```:error_verdict``` as ```true```. This causes the log to end with an error verdict. This verdict may be useful when a log has errors, but no failed verdicts. ```error_verdict.rb```: ```ruby require 'minitest_log' class Test < Minitest::Test def test_demo MinitestLog.new('no_error_verdict.xml') do |log| log.section('This log has no error verdict.') do populate_the_log(log) end end MinitestLog.new('error_verdict.xml', :error_verdict => true) do |log| log.section('This log has an error verdict.') do populate_the_log(log) end end end def populate_the_log(log) log.verdict_assert?(:pass, true) log.verdict_assert?(:fail, false) log.section('My error-producing section', :rescue) do raise Exception.new('Boo!') end end end ``` ```no_error_verdict.xml```: ```xml ``` ```error_verdict.xml```: ```xml ``` #### Backtrace Filter By default, a backtrace omits entries containing the token ```minitest```. This keeps the backtrace focussed on your code instead of the gems' code. Override that behavior by specifying ioption ```:backtrace_filter``` with a ```Regexp``` object. Entries matching that pattern will be omitted from the backtrace. ```backtrace_filter.rb```: ```ruby require 'minitest_log' class Test < Minitest::Test def test_demo MinitestLog.new('default_backtrace_filter.xml') do |log| fail 'Boo!' end MinitestLog.new('custom_backtrace_filter.xml', :backtrace_filter => /xxx/) do |log| fail 'Boo!' end end end ``` ```default_backtrace_filter.xml```: ```xml Boo! ``` ```custom_backtrace_filter.xml```: ```xml Boo! ``` ## Data Put data onto the log using method ```:put_data```. A data object ```obj``` is treated as follows: - If ```obj.kind_of?(String)```, it is treated as a [String](#strings) - Otherwise if ```obj.respond_to?(:each_pair)```, it is treated as [Hash-like](#hash-like-objects). - Otherwise, it ```obj.respond_to?(:each_with_index```, it is treated as [Array-like](#array-like-objects). - Otherwise, it is treated as "[other](#other-objects)". ### Strings An object that is a ```kind_of?(String)``` is logged simply. ```kind_of_string.rb```: ```ruby require 'minitest_log' class Example < MiniTest::Test def test_example MinitestLog.new('kind_of_string.xml') do |log| log.section('Objects that are a kind_of?(String)') do string = 'When you come to a fork in the road, take it. -- Yogi Berra' log.put_data('My string', string) end end end end ``` ```kind_of_string.xml```: ```xml When you come to a fork in the road, take it. -- Yogi Berra ``` ### Hash-Like Objects Otherwise, an object that ```respond_to?(:each_pair)``` is logged as name-value pairs. ```each_pair.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_example MinitestLog.new('each_pair.xml') do |log| log.section('Objects logged using :each_pair') do log.section('Hash') do hash = { :name => 'Ichabod Crane', :address => '14 Main Street', :city => 'Sleepy Hollow', :state => 'NY', :zipcode => '10591', } log.put_data('My hash', hash) end log.section('Struct') do Struct.new('Foo', :x, :y, :z) foo = Struct::Foo.new(0, 1, 2) log.put_data('My struct', foo) end end end end end ``` ```each_pair.xml```: ```xml Ichabod Crane address => 14 Main Street city => Sleepy Hollow state => NY zipcode => 10591 ]]> 0 y => 1 z => 2 ]]> ``` ### Array-Like Objects Otherwise, an object that ```respond_to?(:each_with_index)``` is logged as a numbered list. ```each_with_index.rb```: ```ruby require 'set' require 'minitest_log' class Example < MiniTest::Test def test_example MinitestLog.new('each_with_index.xml') do |log| log.section('Objects logged using :each_with_index') do log.section('Array') do array = %w/apple orange peach banana strawberry pear/ log.put_data('My array', array) end log.section('Set') do set = Set.new(%w/baseball football basketball hockey/) log.put_data('My set', set) end end end end end ``` ```each_with_index.xml```: ```xml ``` ### Other Objects Otherwise, the logger tries, successively, to log the object using ```:to_s```, ```:inspect```, and ```:__id__```. If all that fails, the logger raises an exception (which is not illustrated here). ```to_s.rb```: ```ruby require 'uri' require 'minitest_log' class Example < MiniTest::Test def test_example MinitestLog.new('to_s.xml') do |log| log.section('Objects logged using :to_s') do log.put_data('My integer', 0) log.put_data('My exception', Exception.new('Boo!')) log.put_data('My regexp', /Bar/) log.put_data('My time', Time.now) log.put_data('My uri,', URI('https://www.github.com')) end end end end ``` ```to_s.xml```: ```xml 0 Boo! (?-mix:Bar) 2019-06-01 15:07:01 -0500 https://www.github.com ``` ## Verdicts Use ```MinitestLog``` verdict methods to log details of ```Minitest``` assertions. Each verdict method in ```MinitestLog``` is a wrapper for a corresponding ```Minitest``` assertion (or refutation). An important difference between an assertion and a verdict is that a failed verdict does not exit the test. Instead, the verdict method logs the details for the assertion, regardless of the outcome, and continues test execution. The verdict method returns ```true``` or ```false``` to indicate whether the verdict succeeded or failed. The arguments for the verdict method and its assert method are the same, except that the verdict method adds a leading verdict identifier: ```ruby assert_equal(exp, act) verdict_assert_equal?('verdict_id', exp, act) ``` Like an assertion, a verdict also accepts an optional trailing message string. The verdict identifier: - Is commonly a string or a symbol, but may be any object that responds to ```:to_s```. - Must be unique among the verdict identifiers in its *test method* (but not necessarily in its *test class*.) Each verdict method has a shorter alias -- ```va``` substituting for ```verdict_assert```, and ```vr``` substituting for ```verdict_refute```: Example verdict (long form and alias): ```ruby log.verdict_assert_equal?(:my_verdict_id, exp, act, 'My message') log.va_equal?(:my_verdict_id, exp, act, 'My message') ``` The shorter alias not only saves keystrokes, but also *really*, *really* helps your editor with code completion. Verdict methods are described below. For each, the following is given: - The method's syntax. - An example test using the method, including both passing and failing verdicts. - The log output by the example test. - Descriptive text, adapted from [docs.ruby-lang.org](https://docs.ruby-lang.org/en/2.1.0/MiniTest/Assertions.html) ### Assert Verdicts #### verdict_assert? ```ruby verdict_assert?(id, test, msg = nil) va?(id, test, msg = nil) ``` Fails unless ```test``` is a true value. ```verdict_assert.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_assert.xml') do |log| log.verdict_assert?(:one_id, true, 'One message') log.verdict_assert?(:another_id, false, 'Another message') end end end ``` ```verdict_assert.xml```: ```xml ``` #### verdict_assert_empty? ```ruby verdict_assert_empty?(id, obj, msg = nil) va_empty?(id, obj, msg = nil) ``` Fails unless ```obj``` is empty. ```verdict_assert_empty.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_assert_empty.xml') do |log| log.verdict_assert_empty?(:one_id, [], 'One message') log.verdict_assert_empty?(:another_id, [:a], 'Another message') end end end ``` ```verdict_assert_empty.xml```: ```xml ``` #### verdict_assert_equal? ```ruby verdict_assert_equal?(id, exp, act, msg = nil) va_equal?(id, exp, act, msg = nil) ``` Fails unless ```exp == act```. For floats use verdict_assert_in_delta?. ```verdict_assert_equal.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_assert_equal.xml') do |log| log.verdict_assert_equal?(:one_id, 0, 0, 'One message') log.verdict_assert_equal?(:another_id, 0, 1, 'Another message') end end end ``` ```verdict_assert_equal.xml```: ```xml ``` #### verdict_assert_in_delta? ```ruby verdict_assert_in_delta?(id, exp, act, delta = 0.001, msg = nil) va_in_delta?(id, exp, act, delta = 0.001, msg = nil) ```` For comparing Floats. Fails unless ```exp``` and ```act``` are within ```delta``` of each other. ```verdict_assert_in_delta.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_assert_in_delta.xml') do |log| log.verdict_assert_in_delta?(:one_id, 0, 0, 1, 'One message') log.verdict_assert_in_delta?(:another_id, 0, 2, 1, 'Another message') end end end ``` ```verdict_assert_in_delta.xml```: ```xml ``` #### verdict_assert_in_epsilon? ```ruby verdict_assert_in_epsilon?(id, a, b, epsilon = 0.001, msg = nil) va_in_epsilon?(id, a, b, epsilon = 0.001, msg = nil) ``` For comparing Floats. Fails unless ```exp``` and ```act``` have a relative error less than ```epsilon```. ```verdict_assert_in_epsilon.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_assert_in_epsilon.xml') do |log| log.verdict_assert_in_epsilon?(:one_id, 3, 2, 1, 'One message') log.verdict_assert_in_epsilon?(:another_id, 3, 2, 0, 'Another message') end end end ``` ```verdict_assert_in_epsilon.xml```: ```xml ``` #### verdict_assert_includes? ```ruby verdict_assert_includes?(id, collection, obj, msg = nil) va_includes?(id, collection, obj, msg = nil) ``` Fails unless ```collection``` includes ```obj```. ```verdict_assert_includes.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_assert_includes.xml') do |log| log.verdict_assert_includes?(:one_id, [:a, :b, :c], :b, 'One message') log.verdict_assert_includes?(:another_id, [:a, :b, :c], :d, 'Another message') end end end ``` ```verdict_assert_includes.xml```: ```xml ``` #### verdict_assert_instance_of? ```ruby verdict_assert_instance_of?(id, cls, obj, msg = nil) va_instance_of?(id, cls, obj, msg = nil) ``` Fails unless ```obj``` is an instance of ```cls```. ```verdict_assert_instance_of.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_assert_instance_of.xml') do |log| log.verdict_assert_instance_of?(:one_id, String, 'my_string', 'One message') log.verdict_assert_instance_of?(:another_id, Integer, 'my_string', 'another message') end end end ``` ```verdict_assert_instance_of.xml```: ```xml ``` #### verdict_assert_kind_of? ```ruby verdict_assert_kind_of?(id, cls, obj, msg = nil) va_kind_of?(id, cls, obj, msg = nil) ``` Fails unless ```obj``` is a kind of ```cls```. ```verdict_assert_kind_of.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_assert_kind_of.xml') do |log| log.verdict_assert_kind_of?(:one_id, Numeric, 1.0, 'One message') log.verdict_assert_kind_of?(:another_id, String, 1.0, 'Another message') end end end ``` ```verdict_assert_kind_of.xml```: ```xml ``` #### verdict_assert_match? ```ruby verdict_assert_match?(id, matcher, obj, msg = nil) va_match?(id, matcher, obj, msg = nil) ``` Fails unless ```matcher =~ obj```. ```verdict_assert_match.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_assert_match.xml') do |log| log.verdict_assert_match?(:one_id, /foo/, 'food', 'One message') log.verdict_assert_match?(:another_id, /foo/, 'feed', 'Another message') end end end ``` ```verdict_assert_match.xml```: ```xml ``` #### verdict_assert_nil? ```ruby verdict_assert_nil?(id, obj, msg = nil) va_nil?(id, obj, msg = nil) ``` Fails unless ```obj``` is nil. ```verdict_assert_nil.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_assert_nil.xml') do |log| log.verdict_assert_nil?(:one_id, nil, 'One message') log.verdict_assert_nil?(:another_id, :a, 'Another message') end end end ``` ```verdict_assert_nil.xml```: ```xml ``` #### verdict_assert_operator? ```ruby verdict_assert_operator?(id, o1, op, o2 = UNDEFINED, msg = nil) va_operator?(id, o1, op, o2 = UNDEFINED, msg = nil) ```` For testing with binary operators. ```verdict_assert_operator.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_assert_operator.xml') do |log| log.verdict_assert_operator?(:one_id, 3, :<=, 4, 'One message') log.verdict_assert_operator?(:another_id, 5, :<=, 4, 'Another message') end end end ``` ```verdict_assert_operator.xml```: ```xml ``` #### verdict_assert_output? ```ruby verdict_assert_output?(id, stdout = nil, stderr = nil) { || ... } va_output?(id, stdout = nil, stderr = nil) { || ... } ``` Fails if ```stdout``` or ```stderr``` do not output the expected results. Pass in ```nil``` if you don't care about that streams output. Pass in ```''``` if you require it to be silent. Pass in a regexp if you want to pattern match. NOTE: this uses capture_io, not capture_subprocess_io. ```verdict_assert_output.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_assert_output.xml') do |log| log.verdict_assert_output?(:one_id, stdout = 'Foo', stderr = 'Bar') do $stdout.write('Foo') $stderr.write('Bar') end log.verdict_assert_output?(:another_id, stdout = 'Bar', stderr = 'Foo') do $stdout.write('Foo') $stderr.write('Bar') end end end end ``` ```verdict_assert_output.xml```: ```xml ``` #### verdict_assert_predicate? ```ruby verdict_assert_predicate?(id, o1, op, msg = nil) va_predicate?(id, o1, op, msg = nil) ``` For testing with predicates. ```verdict_assert_predicate.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_assert_predicate.xml') do |log| log.verdict_assert_predicate?(:one_id, '', :empty?, 'One message') log.verdict_assert_predicate?(:another_id, 'x', :empty?, 'Another message') end end end ``` ```verdict_assert_predicate.xml```: ```xml ``` #### verdict_assert_raises? ```ruby verdict_assert_raises?(id, *exp) { || ... } va_raises?(id, *exp) { || ... } ``` Fails unless the block raises one of ```exp```. Returns the exception matched so you can check the message, attributes, etc. ```verdict_assert_raises.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_assert_raises.xml') do |log| log.verdict_assert_raises?(:one_id, RuntimeError, 'One message') do raise RuntimeError.new('Boo!') end log.verdict_assert_raises?(:another_id, RuntimeError, 'Another message') do raise Exception.new('Boo!') end end end end ``` ```verdict_assert_raises.xml```: ```xml ``` #### verdict_assert_respond_to? ```ruby verdict_assert_respond_to?(id, obj, meth, msg = nil) va_respond_to?(id, obj, meth, msg = nil) ``` Fails unless ```obj``` responds to ```meth```. ```verdict_assert_respond_to.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_assert_respond_to.xml') do |log| log.verdict_assert_respond_to?(:one_id, 0, :succ, 'One message') log.verdict_assert_respond_to?(:another_id, 0, :empty?, 'Another message') end end end ``` ```verdict_assert_respond_to.xml```: ```xml ``` #### verdict_assert_same? ```ruby verdict_assert_same?(id, exp, act, msg = nil) va_same?(id, exp, act, msg = nil) ``` Fails unless ```exp``` and ```act``` are ```equal?```. ```verdict_assert_same.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_assert_same.xml') do |log| log.verdict_assert_same?(:one_id, :foo, :foo, 'One message') log.verdict_assert_same?(:another_id, 'foo', 'foo', 'Another message') end end end ``` ```verdict_assert_same.xml```: ```xml ``` #### verdict_assert_silent? ```ruby verdict_assert_silent?(id) { || ... } va_silent?(id) { || ... } ``` Fails if the block outputs anything to ```stderr``` or ```stdout```. ```verdict_assert_silent.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_assert_silent.xml') do |log| log.verdict_assert_silent?(:one_id) do end log.verdict_assert_silent?(:another_id) do $stdout.write('Foo') end end end end ``` ```verdict_assert_silent.xml```: ```xml ``` #### verdict_assert_throws? ```ruby verdict_assert_throws?(id, sym, msg = nil) { || ... } va_throws?(id, sym, msg = nil) { || ... } ``` Fails unless the block throws ```sym```. ```verdict_assert_throws.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_assert_throws.xml') do |log| log.verdict_assert_throws?(:one_id, :foo, 'One message') do throw :foo end log.verdict_assert_throws?(:another_id, :foo, 'Another message') do throw :bar end end end end ``` ```verdict_assert_throws.xml```: ```xml ``` ### Refute Verdicts #### verdict_refute? ```ruby verdict_refute?(id, test, msg = nil) vr?(id, test, msg = nil) ``` Fails if ```test``` is a true value. ```verdict_refute.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_refute.xml') do |log| log.verdict_refute?(:one_id, false, 'One message') log.verdict_refute?(:another_id, true, 'Another message') end end end ``` ```verdict_refute.xml```: ```xml ``` #### verdict_refute_empty? ```ruby verdict_refute_empty?(id, obj, msg = nil) vr_empty?(id, obj, msg = nil) ``` Fails if ```obj``` is empty. ```verdict_refute_empty.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_refute_empty.xml') do |log| log.verdict_refute_empty?(:one_id, [:a], 'One message') log.verdict_refute_empty?(:another_id, [], 'Another message') end end end ``` ```verdict_refute_empty.xml```: ```xml ``` #### verdict_refute_equal? ```ruby verdict_refute_equal?(id, exp, act, msg = nil) vr_equal?(id, exp, act, msg = nil) ``` Fails if ```exp == act```. For floats use verdict_refute_in_delta?. ```verdict_refute_equal.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_refute_equal.xml') do |log| log.verdict_refute_equal?(:one_id, 0, 1, 'One message') log.verdict_refute_equal?(:another_id, 0, 0, 'Another message') end end end ``` ```verdict_refute_equal.xml```: ```xml ``` #### verdict_refute_in_delta? ```ruby verdict_refute_in_delta?(id, exp, act, delta = 0.001, msg = nil) vr_in_delta?(id, exp, act, delta = 0.001, msg = nil) ```` For comparing Floats. Fails if ```exp``` is within ```delta``` of ```act```. ```verdict_refute_in_delta.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_refute_in_delta.xml') do |log| log.verdict_refute_in_delta?(:one_id, 0, 2, 1, 'One message') log.verdict_refute_in_delta?(:another_id, 0, 0, 1, 'Another message') end end end ``` ```verdict_refute_in_delta.xml```: ```xml ``` #### verdict_refute_in_epsilon? ```ruby verdict_ refute_in_epsilon?(id, a, b, epsilon = 0.001, msg = nil) vr_in_epsilon?(id, a, b, epsilon = 0.001, msg = nil) ``` For comparing Floats. Fails if ```exp``` and ```act``` have a relative error less than ```epsilon```. ```verdict_refute_in_epsilon.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_refute_in_epsilon.xml') do |log| log.verdict_refute_in_epsilon?(:one_id, 3, 2, 0, 'One message') log.verdict_refute_in_epsilon?(:another_id, 3, 2, 1, 'Another message') end end end ``` ```verdict_refute_in_epsilon.xml```: ```xml ``` #### verdict_refute_includes? ```ruby verdict_refute_includes?(id, collection, obj, msg = nil) vr_includes?(id, collection, obj, msg = nil) ``` Fails if ```collection``` includes ```obj```. ```verdict_refute_includes.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_refute_includes.xml') do |log| log.verdict_refute_includes?(:one_id, [:a, :b, :c], :d, 'One message') log.verdict_refute_includes?(:another_id, [:a, :b, :c], :b, 'Another message') end end end ``` ```verdict_refute_includes.xml```: ```xml ``` #### verdict_refute_instance_of? ```ruby verdict_refute_instance_of?(id, cls, obj, msg = nil) vr_instance_of?(id, cls, obj, msg = nil) ``` Fails if ```obj``` is an instance of ```cls```. ```verdict_refute_instance_of.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_refute_instance_of.xml') do |log| log.verdict_refute_instance_of?(:one_id, Integer, 'my_string', 'One message') log.verdict_refute_instance_of?(:another_id, String, 'my_string', 'another message') end end end ``` ```verdict_refute_instance_of.xml```: ```xml ``` #### verdict_refute_kind_of? ```ruby verdict_refute_kind_of?(id, cls, obj, msg = nil) vr_kind_of?(id, cls, obj, msg = nil) ``` Fails if ```obj``` is a kind of ```cls```. ```verdict_refute_kind_of.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_refute_kind_of.xml') do |log| log.verdict_refute_kind_of?(:one_id, String, 1.0, 'One message') log.verdict_refute_kind_of?(:another_id, Numeric, 1.0, 'Another message') end end end ``` ```verdict_refute_kind_of.xml```: ```xml ``` #### verdict_refute_match? ```ruby verdict_refute_match?(id, matcher, obj, msg = nil) vr_match?(id, matcher, obj, msg = nil) ``` Fails if ```matcher =~ obj```. ```verdict_refute_match.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_refute_match.xml') do |log| log.verdict_refute_match?(:one_id, /foo/, 'feed', 'One message') log.verdict_refute_match?(:another_id, /foo/, 'food', 'Another message') end end end ``` ```verdict_refute_match.xml```: ```xml ``` #### verdict_refute_nil? ```ruby verdict_refute_nil?(id, obj, msg = nil) vr_nil?(id, obj, msg = nil) ``` Fails if ```obj``` is nil. ```verdict_refute_nil.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_refute_nil.xml') do |log| log.verdict_refute_nil?(:one_id, :a, 'One message') log.verdict_refute_nil?(:another_id, nil, 'Another message') end end end ``` ```verdict_refute_nil.xml```: ```xml ``` #### verdict_refute_operator? ```ruby verdict_refute_operator?(id, o1, op, o2 = UNDEFINED, msg = nil) vr_operator?(id, o1, op, o2 = UNDEFINED, msg = nil) ```` Fails if ```o1``` is not ```op``` ```o2```. ```verdict_refute_operator.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_refute_operator.xml') do |log| log.verdict_refute_operator?(:one_id, 5, :<=, 4, 'One message') log.verdict_refute_operator?(:another_id, 3, :<=, 4, 'Another message') end end end ``` ```verdict_refute_operator.xml```: ```xml ``` #### verdict_refute_predicate? ```ruby verdict_refute_predicate?(id, o1, op, msg = nil) vr_predicate?(id, o1, op, msg = nil) ``` For testing with predicates. ```verdict_refute_predicate.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_refute_predicate.xml') do |log| log.verdict_refute_predicate?(:one_id, 'x', :empty?, 'One message') log.verdict_refute_predicate?(:another_id, '', :empty?, 'Another message') end end end ``` ```verdict_refute_predicate.xml```: ```xml ``` #### verdict_refute_respond_to? ```ruby verdict_refute_respond_to?(id, obj, meth, msg = nil) vr_respond_to?(id, obj, meth, msg = nil) ``` Fails if ```obj``` responds to ```meth```. ```verdict_refute_respond_to.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_refute_respond_to.xml') do |log| log.verdict_refute_respond_to?(:one_id, 0, :empty?, 'One message') log.verdict_refute_respond_to?(:another_id, 0, :succ, 'Another message') end end end ``` ```verdict_refute_respond_to.xml```: ```xml ``` #### verdict_refute_same? ```ruby verdict_refute_same?(id, exp, act, msg = nil) vr_same?(id, exp, act, msg = nil) ``` Fails if ```exp``` is the same (by object identity) as ```act```. ```verdict_refute_same.rb```: ```ruby require 'minitest_log' class Example < Minitest::Test def test_demo_verdict MinitestLog.new('verdict_refute_same.xml') do |log| log.verdict_refute_same?(:one_id, 'foo', 'foo', 'One message') log.verdict_refute_same?(:another_id, :foo, :foo, 'Another message') end end end ``` ```verdict_refute_same.xml```: ```xml ``` ## Tips ### Use Short Verdict Aliases Use the short alias for a verdict method, to: - Have less source code. - Allow code-completion to work *much* better (completion select list gets shorter *much* sooner). Examples: - ```log.va_equal?```, not ```log.verdict assert_equal?```. - ```log.vr_empty?```, not ```log.verdict_refute_empty?```. ### Avoid Failure Clutter Use verdict return values (```true```/```false```) to omit verdicts that would definitely fail. This can greatly simplify your test results. In the example below, the test attempts to create a user. If the create succeeds, the test further validates, then deletes the user. However, if the create fails, the test does not attempt to validate or delete the user (which attempts would fail, and might raise exceptions). Thus, assuming a failed create returns ```nil```: ```ruby user_name = 'Bill Jones' user = SomeApi.create_user(user_name) if log.verdict_refute_nil?(:user_created, user) log.verdict_assert_equal?(:user_name, user_name, user.name) SomeApi.delete_user(user.id) end ``` ### Facilitate Post-Processing If your logs will be parsed in post-processing, you can make that go smoother by creating the logs with certain options: - ```:xml_indentation => -1```: so that there's no log-generated whitespace. (But you'll still see the same indented display in your browser.) - ```:summary => true```: so that the counts are already computed. - ```:error_verdict => true```: so that a log that has errors will also have at least one failed verdict. See [Options](#options) ## Oh, and Tests This project's tests generate 135 [logs and other output files](../../tree/master/test/actual), performing 484 verifications. - [log_test.rb](../../tree/master/test/log_test.rb) - [verdict_test.rb](../../tree/master/test/verdict_test.rb) ## About This README This README page is kept "green" because in addition to the tests, there's a rake task that: - Executes the [50 example scripts](markdown/readme), capturing their output. - Uses file inclusion to assemble this ```README.md``` file. Shameless plug: GitHub flavored markdown does not support file inclusion, but my Ruby gem [markdown_helper](https://rubygems.org/gems/markdown_helper) does. It's a [GitHub project](https://github.com/BurdetteLamar/markdown_helper#markdown-helper), too.