Blackstart
Blackstart is a small, subdued library for automated testing in Ruby. It
doesn't depend on anything beyond the Ruby platform and doesn't modify the
environment outside its conventional namespace. It's tested with a primitive
program, not via another automated-testing library.
The blackstart is a small, subdued bird that eats bugs. A black start is when a
power plant starts up without relying on the electrical grid.
Here's an example of a test program that uses Blackstart:
require "blackstart"
exit Blackstart.run Blackstart.add { |adder|
adder.call do
unless "Hello, World!" == ["He", "", "o, Wor", "d!"].join("l")
raise "join did not work as expected"
end
end
adder.call do
unless "sample" == "simple".gsub(/i/, "a")
raise "gsub did not work as expected"
end
end
}
Blackstart.add adds procs to a collection and returns that collection. In the
example, each proc is designed to run a test and, if it fails, raise an error
to signal this.
Blackstart.run runs a sequence of tests and reports information about any that
fail -- that is, any that raise an error. In the example, failure information
would be written to the standard output stream. It returns false if there were
failures; otherwise, it returns true.
The library provides little beyond this, but it's easy to do relatively
sophisticated things with it. Some examples follow.
- Exiting with the appropriate status
Blackstart.run returns false if there were any failures; otherwise, it returns
true. If you use this as the argument to Kernel#exit, your test program will
exit with a successful status only if there were no failures.
- Defining helpers
You may want all your tests to be able to assert something, generate test data,
or perform some other task by sending a message. You could implement this
statelessly in a singleton object or all instances of Object, for example, but
there's another option that may be more convenient. Blackstart.run calls each
test proc in the context of a new Blackstart::Context instance. You can define
instance methods in that class like this:
class Blackstart::Context
def assert boolean
raise "assertion failed" unless boolean
end
def make_products
@cabbage = { :description => "head of cabbage", :price => 125 }
@orange = { :description => "Cara cara navel orange", :price => 100 }
nil
end
end
All of your tests will be able to use them by sending messages to self. These
methods can see and modify the test's state, which is disposable: it will be
discarded after the test is complete and so will not affect later tests.
- Running code before and after each test
You can run code before and after each test by defining a custom
Blackstart::Context#instance_exec. For example:
class Blackstart::Context
def instance_exec(*)
puts "before test"
@variable = "example"
super
ensure
puts "after test"
end
end
- Building a test collection in stages
You can build your collection of test objects in stages and run it at the end.
For example:
require "blackstart"
TEST_OBJECTS = []
Blackstart.add TEST_OBJECTS do |adder|
adder.call do
unless "Hello, World!" == ["He", "", "o, Wor", "d!"].join("l")
raise "join did not work as expected"
end
end
end
Blackstart.add TEST_OBJECTS do |adder|
adder.call do
unless "sample" == "simple".gsub(/i/, "a")
raise "gsub did not work as expected"
end
end
end
exit Blackstart.run TEST_OBJECTS
This makes it straightforward to define your tests in multiple files that you
load in your test program.
- Running tests in random order
Blackstart.run runs a sequence of tests in order, but you can pass it a
randomly-ordered sequence. Array#shuffle may be helpful for this. If you do
this, you may also want to print the random seed at the start of your program
so you can re-run your tests in the same order.
- Reporting detailed test descriptions
After a test fails, Blackstart.run writes a description of the test to the
output stream. It gets this description by converting the test object to a
string. When the test object is an instance of Proc, which is what
Blackstart.add normally produces, this string typically includes the file path
and line number where it was defined: helpful, but it won't be immediately
clear what was being tested.
You can improve this by making your own test objects instead of using
Blackstart.add. Blackstart.run does not strictly need a sequence of Proc
instances; it only needs a sequence of objects that convert to Proc instances
in response to to_proc messages. Your custom test objects can respond to to_s
with detailed descriptions instead of mere file paths and line numbers.
- Handling failures differently
Blackstart.run handles each failure by sending a puts message to the output
stream, one of its parameters. By default, this is $stdout, so the default
behavior is to print unadorned failure information to standard output. But you
can specify any object as the output stream -- even if it's not really a stream
-- allowing you to handle failure information however you want.
Blackstart is a small, subdued library for automated testing in Ruby. It
doesn't depend on anything beyond the Ruby platform and doesn't modify the
environment outside its conventional namespace. It's tested with a primitive
program, not via another automated-testing library.
The blackstart is a small, subdued bird that eats bugs. A black start is when a
power plant starts up without relying on the electrical grid.
Here's an example of a test program that uses Blackstart:
require "blackstart"
exit Blackstart.run Blackstart.add { |adder|
adder.call do
unless "Hello, World!" == ["He", "", "o, Wor", "d!"].join("l")
raise "join did not work as expected"
end
end
adder.call do
unless "sample" == "simple".gsub(/i/, "a")
raise "gsub did not work as expected"
end
end
}
Blackstart.add adds procs to a collection and returns that collection. In the
example, each proc is designed to run a test and, if it fails, raise an error
to signal this.
Blackstart.run runs a sequence of tests and reports information about any that
fail -- that is, any that raise an error. In the example, failure information
would be written to the standard output stream. It returns false if there were
failures; otherwise, it returns true.
The library provides little beyond this, but it's easy to do relatively
sophisticated things with it. Some examples follow.
- Exiting with the appropriate status
Blackstart.run returns false if there were any failures; otherwise, it returns
true. If you use this as the argument to Kernel#exit, your test program will
exit with a successful status only if there were no failures.
- Defining helpers
You may want all your tests to be able to assert something, generate test data,
or perform some other task by sending a message. You could implement this
statelessly in a singleton object or all instances of Object, for example, but
there's another option that may be more convenient. Blackstart.run calls each
test proc in the context of a new Blackstart::Context instance. You can define
instance methods in that class like this:
class Blackstart::Context
def assert boolean
raise "assertion failed" unless boolean
end
def make_products
@cabbage = { :description => "head of cabbage", :price => 125 }
@orange = { :description => "Cara cara navel orange", :price => 100 }
nil
end
end
All of your tests will be able to use them by sending messages to self. These
methods can see and modify the test's state, which is disposable: it will be
discarded after the test is complete and so will not affect later tests.
- Running code before and after each test
You can run code before and after each test by defining a custom
Blackstart::Context#instance_exec. For example:
class Blackstart::Context
def instance_exec(*)
puts "before test"
@variable = "example"
super
ensure
puts "after test"
end
end
- Building a test collection in stages
You can build your collection of test objects in stages and run it at the end.
For example:
require "blackstart"
TEST_OBJECTS = []
Blackstart.add TEST_OBJECTS do |adder|
adder.call do
unless "Hello, World!" == ["He", "", "o, Wor", "d!"].join("l")
raise "join did not work as expected"
end
end
end
Blackstart.add TEST_OBJECTS do |adder|
adder.call do
unless "sample" == "simple".gsub(/i/, "a")
raise "gsub did not work as expected"
end
end
end
exit Blackstart.run TEST_OBJECTS
This makes it straightforward to define your tests in multiple files that you
load in your test program.
- Running tests in random order
Blackstart.run runs a sequence of tests in order, but you can pass it a
randomly-ordered sequence. Array#shuffle may be helpful for this. If you do
this, you may also want to print the random seed at the start of your program
so you can re-run your tests in the same order.
- Reporting detailed test descriptions
After a test fails, Blackstart.run writes a description of the test to the
output stream. It gets this description by converting the test object to a
string. When the test object is an instance of Proc, which is what
Blackstart.add normally produces, this string typically includes the file path
and line number where it was defined: helpful, but it won't be immediately
clear what was being tested.
You can improve this by making your own test objects instead of using
Blackstart.add. Blackstart.run does not strictly need a sequence of Proc
instances; it only needs a sequence of objects that convert to Proc instances
in response to to_proc messages. Your custom test objects can respond to to_s
with detailed descriptions instead of mere file paths and line numbers.
- Handling failures differently
Blackstart.run handles each failure by sending a puts message to the output
stream, one of its parameters. By default, this is $stdout, so the default
behavior is to print unadorned failure information to standard output. But you
can specify any object as the output stream -- even if it's not really a stream
-- allowing you to handle failure information however you want.