]> Dfect 0.1.0






Contents

Examples

  1. A sample unit test



Chapter 1
Introduction

Dfect is an assertion testing library for Ruby that emphasizes a simple assertion vocabulary, instant debuggability of failures, and flexibility in composing tests.

Dfect is exciting because:

  • It has only 5 methods to remember: D F E C T.
  • It lets you debug assertion failures interactively.
  • It keeps a detailed report of assertion failures.
  • It lets you nest tests and execution hooks.
  • It is implemented in a mere 313 lines of code.

These features distinguish Dfect from the competition:

Etymology

Dfect is named after the D F E C T methods it provides.

The name is also play on the word “defect”, whereby the intentional misspelling of “defect” as “dfect” is a defect in itself! ;-)

This wordplay is similar to Mnesia’s play on the word “amnesia”, whereby the intentional omission of the letter “A” indicates forgetfulness—the key characteristic of having amnesia. Clever!

1.1  Logistics

To get help or provide feedback, simply contact the author(s).

Version numbers

Dfect releases are numbered in major.minor.patch form according to the RubyGems rational versioning policy, which can be summarized thus:

What increased in the version number? The increase indicates that the release:
Is backward compatible? Has new features? Has bug fixes?
major No Yes Yes
minor Yes Yes Yes
patch Yes No Yes

1.2  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.

1.3  Credits

Dfect is made possible by contributions from users like you:

Chapter 2
Setup

2.1  Requirements

Your system needs the following software to run Dfect.

SoftwareDescriptionNotes
RubyRuby language interpreterVersion 1.8.6, 1.8.7, and 1.9.1 have been tested successfully.
RubyGemsRuby packaging systemVersion 1.3.1 is required.
ruby-debugInteractive debuggerThis is an optional requirement; IRB will be used if this library is not available.

2.2  Installation

You can install Dfect by running this command:

gem install dfect

2.3  Manifest

You will see the following items inside Dfect’s installation directory:

  • lib/

    • dfect.rb — the main Dfect library.

    • dfect/

      • auto.rb — automates test execution.
  • doc/

    • api/ — API reference documentation.

    • index.erb — source of this user manual.

  • LICENSE — copyright notice and legal conditions.

Chapter 3
Usage

Begin by loading Dfect into your program:

require 'rubygems'
require 'dfect'

You now have access to the Dfect module, which provides mixin-able instance methods that are also directly-callable as class methods:

Dfect.D "hello" do  # D() is a not-mixed-in class method
  puts "world"
end

# the above is same as:

include Dfect

D "hello" do        # D() is a mixed-in instance method
  puts "world"
end

The following sections explain these provided methods in detail.

3.1  Assertions

The following methods take a block parameter and assert something about the result of executing that block. See the API documentation for examples.

MethodDescription
Fassert not true (nil or false)
Eassert that an execption is raised
Cassert that a symbol is thrown
Tassert true (not nil and not false)

Negation

These methods are the opposite of normal assertions.

MethodDescription
F!same as T
E!assert that an execption is not raised
C!assert that a symbol is not thrown
T!same as F

Sampling

These methods allow you to check the outcome of an assertion without the penalty of pass or failure.

MethodDescription
F?returns true if F passes; false otherwise
E?returns true if E passes; false otherwise
C?returns true if C passes; false otherwise
T?returns true if T passes; false otherwise

3.1.1  Failures

When an assertion fails, details about the failure will be shown:

- fail: block must yield true (!nil && !false)
  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()
  vars:
    x: 5
    y: 83
  call:
  - test/simple.rb:17
  - test/simple.rb:3

You will then be placed into a debugger to investigate the failure if the :debug option is enabled in Dfect.options.

Details about all assertion failures and a trace of all tests executed are stored by Dfect and provided by the Dfect.report method.

3.2  Tests

The D() method defines a new test, which is analagous to the describe() environment provided by BDD frameworks like RSpec.

A test may also contain nested tests.

D "outer test" do
  # assertions and logic here

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

3.2.1  Hooks

The D() method provides several entry points (hooks) into the test execution process:

D "outer test" do
  D .<  { puts "before each nested test" }
  D .>  { puts "after  each nested test" }
  D .<< { puts "before all nested tests" }
  D .>> { puts "after  all nested tests" }

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

A hook method may be called multiple times. Each call registers additional logic to execute during the hook:

D .< { puts "do something" }
D .< { puts "do something more!" }

3.2.2  Insulation

Use the singleton class of a temporary object to shield your test logic from Ruby’s global environment, the code being tested, and from other tests:

class << Object.new
  # your test logic here
end

Inside this insulated environment, you are free to:

  • mix-in any modules your test logic needs
  • define your own constants, methods, and classes

For example:

class << Object.new
  include SomeModule
  extend AnotherModule

  YOUR_CONSTANT = 123

  D "your tests here" do
    # your test logic here

    your_helper_method
  end

  def self.your_helper_method
    # your helper logic here

    helper = YourHelperClass.new
    helper.do_something_helpful

    T { 2 + 2 != 5 }
  end

  class YourHelperClass
    # your helper logic here
  end
end

3.3  Execution

You can configure test execution using:

Dfect.options = your_options_hash

You can execute all tests defined thus far using:

Dfect.run

You can stop this execution at any time using:

Dfect.stop

You can view the results of execution using:

puts Dfect.report.to_yaml

See the API documentation for details and examples.

3.3.1  Automatic test execution

require 'rubygems'
require 'dfect/auto'   # <== notice the "auto"

The above code will mix-in the Dfect module into your program and will execute all tests defined by your program before it terminates.

Example 1.  A sample unit test

require 'rubygems'
require 'dfect/auto'

D "a test" do
  D "a nested test" do
  end

  D "another nested test" do
  end

  D "a complex test" do
    y = 83

    D "with more nested tests" do
      x = 5

      T { x > 2 }   # passes
      F { x > 2 }   # fails
      E { x.hello } # passes
    end
  end

  D .< do
    puts "this is executed before every test in this scope"
  end

  D .> do
    puts "this is executed after every test in this scope"
  end

  D .<< do
    puts "this is executed once, before all tests in this scope"
  end

  D .>> do
    puts "this is executed once, after all tests in this scope"
  end
end

Chapter 4
History

4.1  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 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. —François Beausoleil

  • 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.

    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 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.

4.2  Version 0.0.0 (2009-04-13)

This is the first public release of Dfect. It was inspired by Sean O’Halpin’s musing on alternative names for assertion methods.