NAME

detest - Assertion testing library for Ruby

SYNOPSIS

detest [OPTIONS] (FILE|GLOB)…

Command

Loads the given FILE paths and paths matched by the given GLOB patterns into Ruby, then executes all tests defined therein, and finally terminates with an exit status reflecting the number of errors and assertion failures (up to a maximum of 255 to avoid 8-bit unsigned integer overflow) that occurred.

Options

-d, --debug

Launch interactive debugger upon assertion failures.

-h, --help

Display this manual and exit.

-v, --version

Print version number and exit.

Examples

Execute the foo_test.rb and bar_test.rb files in the current directory:

detest foo_test.rb bar_test.rb

Execute all *_test.rb files inside the test/ directory:

detest 'test/*_test.rb'

Execute all *_test.rb files inside or beneath the test/ directory:

detest 'test/**/*_test.rb'

Execute all foo_test.rb and bar_test.rb files inside or beneath the test/ directory:

detest 'test/**/{foo,bar}_test.rb'

DESCRIPTION

Detest is an assertion testing library for [Ruby] that features a simple assertion vocabulary, instant debuggability of failures, and flexibility in composing tests.

Features

  • Only 8 mnemonic method names for you to remember:

    True, False, Nil, Error, Catch, Share, Inform, Describe

  • Lets you debug assertion failures interactively.

  • Lets you nest tests, assertions, and execution hooks.

  • Maintains a detailed report of assertion failures.

  • Implemented in 434 lines of pure Ruby.

Resources

Project website

http://snk.tuxfamily.org/lib/detest/

Announcements feed

http://snk.tuxfamily.org/lib/detest/ann.xml

API documentation

http://snk.tuxfamily.org/lib/detest/api/

Source code (browse online, download, or checkout)

http://github.com/sunaku/detest

Issue tracker (report bugs, request features, get help)

http://github.com/sunaku/detest/issues

INSTALL

Prerequisites

Installing

gem install detest

Upgrading

gem update detest

Removing

gem uninstall detest

USAGE

Begin by loading the Detest library into your program:

require 'rubygems' # might not be necessary; see HACKING
require 'detest'

You can now call methods on the Detest module directly:

Detest.D 'hello' do  # D() is a class method
  puts 'world'
end

or mix-in the module and call its methods implicitly:

include Detest      # mix-in the Detest module

D 'hello' do        # D() is an instance method
  puts 'world'
end

You can configure the test execution process using:

Detest.debug = true # or false

You can execute all tests defined thus far, in depth-first search (DFS) order, using:

Detest.start

You can stop the execution at any time using:

Detest.stop

You can view the results of execution using:

puts Detest.trace.to_yaml
puts Detest.stats.to_yaml

You can clear the results of execution using:

Detest.reset

You can mix-in the Detest module into your program and execute all tests defined by your program before it terminates by simply adding the following line at the top of your program:

require 'detest/auto'

See the API documentation for more information and examples.

ASSERTIONS

Writing assertions

The following methods accept a block parameter and assert something about the result of executing that block. They also accept an optional message, which is shown in their failure reports if they fail.

T()

assert true (not nil and not false) — API documentation

F()

assert not true (nil or false)  — API documentation

N()

assert that the value is nil  — API documentation

E()

assert that an execption is raised  — API documentation

C()

assert that a symbol is thrown  — API documentation

For the T() and F() methods, you may alternatively pass the condition to be asserted as the first argument (instead of passing it as a block). This might result in a more pleasing syntax, depending on your taste:

my_ticket = rand()
winning_ticket = rand()

# passing the condition as a block:
T("I won?!  Dream on.") { my_ticket != winning_ticket }
F("I won?!  Dream on.") { my_ticket == winning_ticket }

# passing the condition as an argument:
T my_ticket != winning_ticket, "I won?!  Dream on."
F my_ticket == winning_ticket, "I won?!  Dream on."

Negating assertions

The following methods are the opposite of normal assertions.

T!()

same as F()  — API documentation

F!()

same as T()  — API documentation

N!()

assert that value is not nil  — API documentation

E!()

assert that an exception is not raised  — API documentation

C!()

assert that a symbol is not thrown  — API documentation

Sampling assertions

The following methods let you check the outcome of an assertion without recording a success or failure in the test execution report.

T?()

returns true if T() passes; false otherwise — API documentation

F?()

returns true if F() passes; false otherwise — API documentation

N?()

returns true if N() passes; false otherwise — API documentation

E?()

returns true if E() passes; false otherwise — API documentation

C?()

returns true if C() passes; false otherwise — API documentation

Assertion failure reports

Assertions failures are reported in the following manner:

- fail: block must yield true (!nil && !false)
  call:
  - test/simple.rb:17
  - test/simple.rb:3
  code: |-
    [12..22] in test/simple.rb
       12
       13     D "with more nested tests" do
       14       x = 5
       15
       16       T { x > 2 }   # passes
    => 17       F { x > 2 }   # fails
       18       E { x.hello } # passes
       19     end
       20   end
       21
       22   # equivalent of before(:each) or setup()
  bind: test/simple.rb:17
  vars:
    x: (Fixnum) 5
    y: (Fixnum) 83

Failure reports are composed of the following sections:

:fail

Description of the assertion failure.

:call

Stack trace leading to the point of failure.

:code

Source code surrounding the point of failure.

:bind

Source location of values in :vars section.

:vars

Local variables visible at the point of failure.

After the failure is reported, you will be placed into a debugger to investigate the failure if the Detest.debug option is enabled.

Assertion failure reports can be accessed at any time within the test execution trace provided by the Detest.trace() method.

TESTS

Defining tests

The D() method creates a new test, which is analagous to the describe keyword in [RSpec] and also to the concept of a "test case" in [xUnit].

D "outer test" do
  # assertions and logic here

  D "inner test" do
    # more assertions and logic here
  end
end

A test may contain nested tests, as illustrated above.

Insulating tests

The D!() method defines a new test that is explicitly insulated from the tests that contain it and also from the top-level Ruby environment.

Inside an insulated test, you are free to mix-in (using the extend keyword, not the include keyword) any modules your test logic needs. You can also define your own constants, methods, classes, and modules here.

Note Root-level calls to the D() method are insulated by default.
Example 1. Insulated and uninsulated tests

When the following test is run:

require 'detest/auto'

D "a root-level test" do
  @outside = 1
  T { defined? @outside }
  T { @outside == 1 }

  D "an inner, non-insulated test" do
    T { defined? @outside }
    T { @outside == 1 }
  end

  D! "an inner, insulated test" do
    F { defined? @outside }
    F { @outside == 1 }

    @inside = 2
    T { defined? @inside }
    T { @inside == 2 }
  end

  F { defined? @inside }
  F { @inside == 2 }
end

Detest will output the following:

---
- a root-level test:
  - an inner, non-insulated test:
  - an inner, insulated test:
---
pass: 10
time: 0.003247271

Sharing tests

The S() method is a mechanism for sharing code. It can be used in two ways:

  1. When called with a block, it shares the given block (under a given identifier) for injection into other tests.

  2. When called without a block, it injects a previously shared block (under a given identifier) into the environment where it is called.

The S!() method is a combination of the two uses of the S() method: it lets you simultaneously share a block of code while injecting it into the environment where that method is called.

The S?() method checks whether any code has been shared under a given identifier.

Example 2. Sharing code between tests

When the following test is run:

require 'detest/auto'

S :knowledge do
  I 'Knowledge is power!'
end

D 'Healer' do
  S :knowledge
end

D 'Warrior' do
  S! :strength do
    I 'Strength is power!'
  end
end

D 'Wizard' do
  S :knowledge
  S :strength
end

D 'King' do
  T { S? :knowledge }
  T { S? :strength }
  F { S? :power }
  I 'Power is power!'
end

Detest will output the following:

---
- Healer:
  - Knowledge is power!
- Warrior:
  - Strength is power!
- Wizard:
  - Knowledge is power!
  - Strength is power!
- King:
  - Power is power!
---
pass: 3
time: 0.002453035

Logging information

The I() method is a mechanism for inserting arbitrary Ruby objects into the test execution report. You can think of this method as being a way to inform someone.

The I!() method starts the interactive debugger at the location where it is called. You can think of this method as being a way to investigate the state of your program.

Example 3. Logging information in the execution report

When the following test is run:

require 'detest/auto'

D 'Wizard' do
  I 'Preparing spell to defeat mortal foes...'
end

D 'Magician' do
  I 'Preparing rabbits to pull from hat...', rand(15)
end

D 'Calculator' do
  I Math::PI, [1, 2, 3, ['a', 'b', 'c']], {:foo => 'bar!'}
end

Detest will output the following:

---
- Wizard:
  - Preparing spell to defeat mortal foes...
- Magician:
  - Preparing rabbits to pull from hat...
  - 9
- Calculator:
  - 3.141592653589793
  - - 1
    - 2
    - 3
    - - a
      - b
      - c
  - foo: bar!
---
time: 0.000969816

Execution hooks

A hook is a scheduled point of entry into the test execution process. The following hook methods allow you to register a block of code to execute when a hook occurs:

D.<()

calls the given block before each child test — API documentation

D.>()

calls the given block after each child test  — API documentation

D.<<()

calls the given block before all child tests — API documentation

D.>>()

calls the given block after all child tests  — API documentation

A hook method can be called multiple times. Each additional call schedules more logic to be executed during the hook:

D .< { puts "do something" }
D .< { puts "do something more!" }
Example 4. Using hooks to perform before and after actions

When the following test is run:

require 'detest/auto'

D "outer test" do
  D .<  { I "(outer hook) before each" }
  D .>  { I "(outer hook) after each" }
  D .<< { I "(outer hook) before all" }
  D .>> { I "(outer hook) after all" }

  D "inner test 1" do
    D .<  { I "(inner hook) before each" }
    D .>  { I "(inner hook) after each" }
    D .<< { I "(inner hook) before all" }
    D .>> { I "(inner hook) after all" }

    D "inner test 1.1" do
      I "hello world"
    end
  end

  D "inner test 2" do
    I "goodbye world"
  end

  D .<  { I "(outer hook) before each, again" }
  D .>  { I "(outer hook) after each, again" }
end

Detest will output the following:

---
- outer test:
  - (outer hook) before all
  - (outer hook) before each
  - (outer hook) before each, again
  - inner test 1:
    - (inner hook) before all
    - (inner hook) before each
    - inner test 1.1:
      - hello world
    - (inner hook) after each
    - (inner hook) after all
  - (outer hook) after each
  - (outer hook) after each, again
  - (outer hook) before each
  - (outer hook) before each, again
  - inner test 2:
    - goodbye world
  - (outer hook) after each
  - (outer hook) after each, again
  - (outer hook) after all
---
time: 0.002630921

EMULATION

Detest can emulate several popular testing libraries:

detest/spec

[RSpec] emulation layer

detest/unit

[Test::Unit] emulation layer

detest/mini

[Minitest] emulation layer

detest/long

Readability emulation layer

Simply require one of these emulation layers into your test suite and you can write your tests using the familiar syntax of the testing library it emulates.

detest/spec

This library emulates [RSpec] by adding the following methods to the Detest module.

after(what, &block)

lib/detest/spec.rb:21

before(what, &block)

lib/detest/spec.rb:10

context(*args, &block)

lib/detest.rb:1166

describe(*args, &block)

lib/detest.rb:1166

it(*args, &block)

lib/detest.rb:1166

detest/unit

This library emulates [Test::Unit] by adding the following methods to the Detest module.

assert(*args, &block)

lib/detest.rb:1166

assert_empty(collection, message=nil)

lib/detest/unit.rb:26

assert_equal(expected, actual, message=nil)

lib/detest/unit.rb:31

assert_in_delta(expected, actual, delta=nil, message=nil)

lib/detest/unit.rb:36

assert_in_epsilon(expected, actual, delta=nil, message=nil)

lib/detest/unit.rb:36

assert_include(item, collection, message=nil)

lib/detest/unit.rb:47

assert_instance_of(klass, object, message=nil)

lib/detest/unit.rb:52

assert_kind_of(klass, object, message=nil)

lib/detest/unit.rb:57

assert_match(pattern, string, message=nil)

lib/detest/unit.rb:67

assert_nil(object, message=nil)

lib/detest/unit.rb:62

assert_not(*args, &block)

lib/detest.rb:1166

assert_not_empty(collection, message=nil)

lib/detest/unit.rb:26

assert_not_equal(expected, actual, message=nil)

lib/detest/unit.rb:31

assert_not_in_delta(expected, actual, delta=nil, message=nil)

lib/detest/unit.rb:36

assert_not_in_epsilon(expected, actual, delta=nil, message=nil)

lib/detest/unit.rb:36

assert_not_include(item, collection, message=nil)

lib/detest/unit.rb:47

assert_not_instance_of(klass, object, message=nil)

lib/detest/unit.rb:52

assert_not_kind_of(klass, object, message=nil)

lib/detest/unit.rb:57

assert_not_match(pattern, string, message=nil)

lib/detest/unit.rb:67

assert_not_nil(object, message=nil)

lib/detest/unit.rb:62

assert_not_operator(object, operator, operand, message=nil)

lib/detest/unit.rb:77

assert_not_raise(*args, &block)

lib/detest/unit.rb:82

assert_not_respond_to(object, query, message=nil)

lib/detest/unit.rb:86

assert_not_same(expected, actual, message=nil)

lib/detest/unit.rb:72

assert_not_send(send_array, message=nil)

lib/detest/unit.rb:95

assert_not_throw(symbol, message=nil, &block)

lib/detest/unit.rb:91

assert_operator(object, operator, operand, message=nil)

lib/detest/unit.rb:77

assert_raise(*args, &block)

lib/detest/unit.rb:82

assert_respond_to(object, query, message=nil)

lib/detest/unit.rb:86

assert_same(expected, actual, message=nil)

lib/detest/unit.rb:72

assert_send(send_array, message=nil)

lib/detest/unit.rb:95

assert_throw(symbol, message=nil, &block)

lib/detest/unit.rb:91

setup(*args, &block)

lib/detest.rb:1166

setup!(*args, &block)

lib/detest.rb:1166

teardown(*args, &block)

lib/detest.rb:1166

teardown!(*args, &block)

lib/detest.rb:1166

test(*args, &block)

lib/detest.rb:1166

detest/mini

This library emulates [Minitest] by adding the following methods to the Detest module.

refute(*args, &block)

lib/detest.rb:1166

refute_empty(collection, message=nil)

lib/detest/unit.rb:26

refute_equal(expected, actual, message=nil)

lib/detest/unit.rb:31

refute_in_delta(expected, actual, delta=nil, message=nil)

lib/detest/unit.rb:36

refute_in_epsilon(expected, actual, delta=nil, message=nil)

lib/detest/unit.rb:36

refute_include(item, collection, message=nil)

lib/detest/unit.rb:47

refute_instance_of(klass, object, message=nil)

lib/detest/unit.rb:52

refute_kind_of(klass, object, message=nil)

lib/detest/unit.rb:57

refute_match(pattern, string, message=nil)

lib/detest/unit.rb:67

refute_nil(object, message=nil)

lib/detest/unit.rb:62

refute_operator(object, operator, operand, message=nil)

lib/detest/unit.rb:77

refute_raise(*args, &block)

lib/detest/unit.rb:82

refute_respond_to(object, query, message=nil)

lib/detest/unit.rb:86

refute_same(expected, actual, message=nil)

lib/detest/unit.rb:72

refute_send(send_array, message=nil)

lib/detest/unit.rb:95

refute_throw(symbol, message=nil, &block)

lib/detest/unit.rb:91

detest/long

This library emulates Readability by adding the following methods to the Detest module.

Catch(*args, &block)

lib/detest.rb:1166

Catch!(*args, &block)

lib/detest.rb:1166

Catch?(*args, &block)

lib/detest.rb:1166

Describe(*args, &block)

lib/detest.rb:1166

Describe!(*args, &block)

lib/detest.rb:1166

Error(*args, &block)

lib/detest.rb:1166

Error!(*args, &block)

lib/detest.rb:1166

Error?(*args, &block)

lib/detest.rb:1166

False(*args, &block)

lib/detest.rb:1166

False!(*args, &block)

lib/detest.rb:1166

False?(*args, &block)

lib/detest.rb:1166

Inform(*args, &block)

lib/detest.rb:1166

Inform!(*args, &block)

lib/detest.rb:1166

Nil(*args, &block)

lib/detest.rb:1166

Nil!(*args, &block)

lib/detest.rb:1166

Nil?(*args, &block)

lib/detest.rb:1166

Share(*args, &block)

lib/detest.rb:1166

Share!(*args, &block)

lib/detest.rb:1166

Share?(*args, &block)

lib/detest.rb:1166

True(*args, &block)

lib/detest.rb:1166

True!(*args, &block)

lib/detest.rb:1166

True?(*args, &block)

lib/detest.rb:1166

HACKING

Prerequisites

Install Ruby libraries necessary for development using [Bundler]:

bundle install

Infrastructure

[Inochi] serves as the project infrastructure for Detest. It handles tasks such as building this help manual and API documentation, and packaging, announcing, and publishing new releases. See its help manual and list of tasks to get started:

inochi --help     # display help manual
inochi --tasks    # list available tasks

$LOAD_PATH setup

Ensure that the lib/ directory is listed in Ruby’s $LOAD_PATH before you use any libraries therein or run any executables in the bin/ directory.

This can be achieved by passing an option to Ruby:

ruby -Ilib bin/detest
irb -Ilib -r detest

Or by setting the $RUBYLIB environment variable:

export RUBYLIB=lib   # bash, ksh, zsh
setenv RUBYLIB lib   # csh
set -x RUBYLIB lib   # fish

ruby bin/detest
irb -r detest

Or by running Ruby through the ruby-wrapper tool.

RubyGems setup

If you use Ruby 1.8 or older, then ensure that RubyGems is activated before you use any libraries in the lib/ directory or run any executables in the bin/ directory.

This can be achieved by passing an option to Ruby:

ruby -rubygems bin/detest
irb -rubygems -r detest

Or by setting the $RUBYOPT environment variable:

export RUBYOPT=-rubygems   # bash, ksh, zsh
setenv RUBYOPT -rubygems   # csh
set -x RUBYOPT -rubygems   # fish

Running tests

Simply execute the included test runner, which sets up Ruby’s $LOAD_PATH for testing, loads the included test/test_helper.rb file, and then evaluates all test/**/*_test.rb files:

ruby test/runner

Its exit status will indicate whether all tests have passed. It may also print additional pass/fail information depending on the testing library used in the test/test_helper.rb file.

Contributing

Fork this project on GitHub and send a pull request.

HISTORY

Version 3.1.3 (2011-04-22)

This release fixes bugs in the Test::Unit emulation layer.

Bug fixes
  • assert_send() did not follow Test::Unit syntax.

  • assert_operator() did not set failure message.

Housekeeping
  • Upgrade to Inochi 6.0.2.

Version 3.1.2 (2010-08-10)

This release adds links to API documentation beside methods mentioned in the help manual and restores metadata that was missing in the gem release package.

Housekeeping
  • Upgrade to Inochi 5.0.1 to fix metadata generation in gemspec.

  • Introduce assertions before tests in help manual.

  • Make use of AsciiDoc admonitions and icon images.

  • Add links into API documentation for core methods.

  • Add test case to ensure that E() returns the exception that was raised.

Version 3.1.1 (2010-08-08)

This release adds forgotten nil assertions and updates the help manual.

Bug fixes
  • Forgot to add nil assertion methods in the detest/long library.

Housekeeping
  • Upgrade to Inochi 5.0.0; the help manual is now written in AsciiDoc.

Version 3.1.0 (2010-07-25)

This release adds N() methods for nil value assertions and renames the project from "DIFECTS" to "Detest" to avoid forming a habit of intentional misspellings.

New features
  • Add N(), N!(), and N?() methods for asserting that a value is nil. This idea comes from Gavin Sinclair’s [Attest] assertion testing library.

Housekeeping
  • Rename project from "DIFECTS" to "Detest".

  • Upgrade project to Inochi 4.0.0 infrastructure.

  • Minor code refactoring and manual revisions.

Version 3.0.1 (2010-07-25)

This release fixes a bug in Ruby 1.8, refactors the code, and better documents test hooks in the manual.

Bug fixes
  • Ruby 1.8 does not pass value to Hash#delete_if(). All failure details were being omitted, instead of just the unavailable ones, as a result.

Housekeeping
  • Refactor variable values hash calculation and pretty printing logic.

  • Put FailureDetails pretty-printing modules into their own namespace.

  • Raise error if closest insulated test cannot be not found.

  • Describe all test hooks and add example to manual, plus small revisions.

Version 3.0.0 (2010-07-24)

This release renames the project from "Dfect" to "DIFECTS", reduces cruft, improves the presentation and debuggability of assertion failures, and revises the manual.

Thank you
  • Gavin Sinclair inspired me to work on this project again!

Incompatible changes
  • Rename project from "Dfect" to "DIFECTS", which stands for:

    Describe, Inform, False, Error, Catch, True, Share

  • Remove [ruby-debug] integration because it is only helpful if you run a program inside it from the very start! That is, you cannot start ruby-debug in the middle of a program and expect it to know about the call stack that lead up to that point in the program. Instead, we now use IRB to inspect program state at the point of failure only.

  • Remove --quiet option because user can pipe to /dev/null instead.

  • Rename run() to start() to better complement stop(). The run() method no longer clears test results; use reset() for that.

  • Rename L() to I() as in "inform" the user.

  • Replace options() with debug().

  • Replace report() with trace() and stats().

  • Rename the difects/full library to difects/long.

  • Do not report instance variables in assertion failures.

New features
  • Improve debuggability by tracking the bindings of all lines of code executed leading up to the point of failure using Ruby’s awesome set_trace_func facility.

    This allows block-less assertions to be debugged with the same level of accuracy as normal block-given assertions:

    x = 1
    y = 3
    T { x == y }  # a block-given assertion
    T x == y      # a block-less assertion
    

    In both cases, you will be able to inspect the local variables x and y!

  • Add I!() method to start the interactive debugger anywhere in your tests.

  • Add reset() to manually clear previous test results.

  • Alias test() to D() in [Test::Unit] emulation layer.

  • Fallback to pp() if to_yaml() fails while reporting failures.

  • Use [OrderedHash] library in Ruby versions older than 1.9 for consistent presentation of information in assertion failures.

  • Show full failure details before starting debugger instead of omitting the backtrace and local variables listing.

  • Use PP to pretty-print variable values in failure details.

  • Omit unavailable information from failure details.

  • Put backtrace above code listing and variables in failure details.

  • Prevent empty array leaf nodes in execution trace.

Bug fixes
  • Make DIFECTS module’s instance methods available as class methods.

  • Always display fail trace before entering debugger.

  • Always clear test execution internals after start().

  • Prevent IRB re-initialization errors when starting debugger.

Housekeeping
  • Clarify how to mix-in modules inside insulated tests in the manual. Thanks to Gavin Sinclair for reporting this issue.

  • Document methods (with hyperlinks to the location in the source code where they are defined) provided by emulation layers in manual.

  • Talk about passing condition as first argument to T and F assertions and provide a code example in the manual.

  • Clean up the code and revise the manual. Yay!

Version 2.2.0 (2010-04-28)

This release adds a UNIX manual page and a sub-library for full method names.

New features
  • Add dfect/full sub-library that provides full name aliases to Dfect’s abbreviated vocabulary:

    Describe, True, False, Error, Catch, Share, Log

  • Run dfect --help to see the UNIX manual page!

Housekeeping
  • Upgrade to Inochi 3.0.0 and revise the help manual.

Version 2.1.0 (2010-03-31)

This release adds a command-line test runner and performs some minor housekeeping.

New features
  • Add bin/dfect executable as command-line interface to this library.

Housekeeping
  • Do not require 'rubygems' before loading the "ruby-debug" library.

  • Upgrade to Inochi 2.0.0-rc2 for managing this project.

Version 2.0.0 (2010-03-21)

This release adds the ability to insulate tests from each other, share code between them, makes the order of parameters consistent in the API, improves user interactivity, fixes some bugs, and revises the user manual.

Incompatible changes
  • Root-level calls to the Dfect::D() method are automatically insulated now.

  • The Dfect::E() methods now expects its optional message parameter to be the last parameter in the parameter list.

  • The Dfect::C() methods now expect their first parameter to be a symbol instead of the optional message to be shown in case of assertion failure.

  • The Dfect::R() has been renamed to Dfect::L(), which is a mnemonic for "Logging".

  • Shorten names of hash keys in the execution trace for brevity and rename :raise key in report statistics to :error.

  • Only the most helpful subset of the failure details is shown before placing the user into a debugger because they can query the omitted information (on demand) inside the debugger.

  • The execution trace is only shown if all tests passed in Dfect::run().

  • The :debug option is now set to Ruby’s $DEBUG global by default.

New features
  • Print failures as they occur instead of waiting until the end.

  • Allow passing condition as argument to true/false assertions instead of requiring the condition to be passed as a code block, and also fall back to the binding of inner-most enclosing test or hook when debugging or constructing a failure report for an assertion that was not given a block.

    This allows you to reduce "line noise" in your tests:

    D "Lottery" do
      winning_ticket = rand()
    
      D "My chances of winning" do
        my_ticket = rand()
        F my_ticket == winning_ticket, "I won?!  Dream on."
      end
    end
    
  • Add Dfect::S() methods for sharing code between tests.

  • Add Dfect::D!() method to explicitly insulate a test from other tests, the top-level Ruby environment, and the code being tested.

  • Add Dfect::info() method which returns the details of the failure that is currently being debugged by the user.

  • Add instance variables to the :vars section of a failure report.

  • Add setup!() and teardown!() methods for before-all and after-all hooks in the dfect/unit emulation library.

  • Add test execution time to statistics hash (under the :time key).

Bug fixes
  • Do not print any output when :quiet option is active.

  • Allow passing multiple strings/objects to Dfect::D() like in RSpec.

  • Make before and after hook methods mixin-able like assertions.

  • Do not assume that Module#to_s is the same as Module#name.

Housekeeping
  • Upgrade to Inochi 2.0.0-rc1 for managing this project.

  • Make emulation libraries modify Dfect module instead of Kernel.

  • Do not pollute the user’s output with our Class#to_yaml workaround.

  • Remove "Motivation" section from user manual. It was too fanatic!

Version 1.1.0 (2009-10-27)

This release adds a new method for emitting status messages and does some internal housekeeping.

Thank you
  • Iñaki Baz Castillo used Dfect and suggested new features.

New features
Housekeeping
  • Remove unused require of delegate standard library in dfect/spec RSpec emulation layer.

  • Mention emulation layers for popular testing libraries.

  • Mention that assertions take an optional message parameter.

  • Replace sample unit test with Dfect test suite.

  • Upgrade user manual to ERBook 9.0.0.

Version 1.0.1 (2009-10-07)

This release fixes a bug in the [Test::Unit] emulation library and revises the user manual.

Bug fixes
  • The parameters for the assert_equal() method in the dfect/unit library were in the wrong order.

Housekeeping
  • Revise user manual to better fit jQuery UI tabs.

  • Justify the use of eval() in emulation libraries.

  • Use simpler Copyright reminder at the top of every file.

  • Make SLOC count in user manual reflect the core library only.

  • Mark code spans with {:lang=ruby} instead of HTML <code/> tags.

  • Open source is for fun, so be nice and speak of "related works" instead of "competitors".

Version 1.0.0 (2009-05-03)

This release improves default choices, adds emulation layers to mimic other testing libraries, and fixes some bugs.

Incompatible changes
  • The :debug option is now enabled by default and is no longer linked to the value of $DEBUG.

  • Dfect.run() now appends to previous results by default. This behavior can be disabled by passing false to the method.

New features
  • Add emulation layers to mimic other testing libraries:

    dfect/unit

    [Test::Unit] emulation layer

    dfect/mini

    [Minitest] emulation layer

    dfect/spec

    [RSpec] emulation layer

Bug fixes
  • Do not blindly replace Class#to_yaml; it might be fixed someday.

Housekeeping
  • Add "Motivation" section in user manual to promote interactive debugging.

  • Add brief History of this project’s inception.

  • Remove redundant assertions for F!() and T!() methods in test suite.

  • Add copyright notice at the top of every file.

Version 0.1.0 (2009-04-28)

This release adds new variations to assertion methods, fixes several bugs, and improves test coverage.

Thank you
  • François Beausoleil contributed patches for both code and tests! :-)

New features
  • Added negation (m!) and sampling (m?) variations to normal assertion methods. These new methods implement assertion functionality missing so far (previously we could not assert that a given exception was NOT thrown) and thereby allow us to fully test Dfect using itself.

  • Added documentation on how to insulate tests from the global Ruby namespace.

Bug fixes
  • The E() method did not consider the case where a block does not raise anything as a failure. Thanks to François Beausoleil for reporting this.

  • When creating a report about an assertion failure, an exception would be thrown if any local variables pointed to an empty array.

  • The Dfect::<() method broke the inheritance-checking behavior of the < class method. I added a bypass to the originial behavior so that RCov::XX can properly generate a report about code that uses Dfect.

  • Added workaround for YAML error that occurs when serializing a class object:

    TypeError: can't dump anonymous class Class
Housekeeping
  • Filled the big holes in test coverage. Everything except the runtime debugging logic is now covered by the unit tests.

Version 0.0.0 (2009-04-13)

For the longest time, I took [Test::Unit] and [RSpec] for granted. They were the epitomy of modern Ruby practice; the insurmountable status quo; immortalized in books, conferences, and blogs alike.

Why would anyone think of using anything remotely different, let alone be foolish enough to write an alternative testing library when these are clearly "good enough"?

Recent experiments in assertion testing libraries smashed my world view:

The status quo was certainly not "good enough", as I had so blindly believed all these years. In fact, they were verbose behemoths that chose to encode endless permutations of conjecture into methods.

Empowered by this revelation and inspired by Sean O’Halpin’s musing on alternative names for assertion methods, I rose to challenge the status quo.

And so I present to you the first public release of "Dfect".

AUTHORS

Suraj N. Kurapati

Credits

François Beausoleil, Gavin Sinclair, Iñaki Baz Castillo, Sean O’Halpin

License

(the ISC license)

Copyright 2009 Suraj N. Kurapati <sunaku@gmail.com>

Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

SEE ALSO