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
- Release notes — history of project releases.
- Source code — obtain via Git or browse online.
- API reference — documentation for source code.
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.
Chapter 2
Setup
2.1 Requirements
Your system needs the following software to run Dfect.
Software | Description | Notes |
---|---|---|
Ruby | Ruby language interpreter | Version 1.8.6, 1.8.7, and 1.9.1 have been tested successfully. |
RubyGems | Ruby packaging system | Version 1.3.1 is required. |
ruby-debug | Interactive debugger | This is an optional requirement; IRB will be used if this library is not available. |
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.
Method | Description |
---|---|
F | assert not true (nil or false ) |
E | assert that an execption is raised |
C | assert that a symbol is thrown |
T | assert true (not nil and not false ) |
Negation
These methods are the opposite of normal assertions.
Method | Description |
---|---|
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.
Method | Description |
---|---|
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.
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.
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
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.