Forget your perfect offering
There is a crack in everything
That's how the light gets in.
—Leonard Cohen
Yesterday, I posted on a trivial little testing library I hacked together. I've put the source online. Get the source through Rob's git repository (see below).
In the meantime, I discovered a problem with the idea of intercepting comparison operators, the technique used by the expect method. Ruby doesn't really have != and !~ methods. Instead, the parser maps (a != b) into !(a == b). This means that the ComparisonProxy cannot intercept calls to either of these. This is a problem because
actually passes, because it becomes expect(1) != 1
!(expect(1) == 1), and the expect method is happy with that.
I'm betting there's a way around this...
Update: 14:26 CDT.
- Rob Sanheim has set up a Git repository for the code. He says
I've put this up on github to watch what forks or releases develop around it.
git clone git://github.com/rsanheim/prag_dave_testing.git -
Michael Neumann suggested a way around the negated == and =~ tests using source inspection:
class ComparatorProxy def ==(obj) # try to get the source code position of the call # and see if it's a != or a == end end




expect(1 != 1) ?
Posted by: Jason Perry | March 14, 2008 at 10:27 AM
Of course, now I look at your library and see- that's now how it's meant to be used at all.
Posted by: Jason Perry | March 14, 2008 at 10:31 AM
I've put this up on github to watch what forks or releases develop around it.
git clone git://github.com/rsanheim/prag_dave_testing.git
Posted by: Rob Sanheim | March 14, 2008 at 01:04 PM
Why not: expect { 1 == 1 }? Would also enable you to rescue Exceptions raised by the code.
Posted by: apeiros | March 15, 2008 at 06:37 AM
Apeiros:
Having expect{ 1 == 1 } wouldn't give me access to the individual values when reporting errors--I can only change the meaning of == after the code being tested has run.
Dave
Posted by: Dave Thomas | March 15, 2008 at 08:24 AM
If SCRIPT_LINES__ is a hash, it will hold all sources parsed after it was set. See lib/debug.rb.
Posted by: Nobu Nakada | March 15, 2008 at 08:53 AM
Nobo-san:
I thought about SCRIPT_LINES__, but decided against it because I'd need to ensure that the testing library got control early. I was also nervous that with a large application I'd always be loading in a lot of code that I may never use. Reopening the source is a fairly small overhead for something that happens infrequently.
Dave
Posted by: Dave Thomas | March 15, 2008 at 09:00 AM
The library also has a problem if an individual test is continued on multiple lines. The report line that begins "the code was:" is incorrect. Correctly identifying the code for the test is an interesting problem.
Posted by: mtcronin | March 18, 2008 at 12:05 PM
I've forked this from rsanheims github and worked a little bit on it. I've patched the negation issue und started to write some tests and a run helper for testing itself.
For example:
require 'test_helper'
testing "negated comparison with !=" do
result = run { expect(1) != 1 }
expect(result) =~ /but\s1\s==\s1/
end
You can get it here:
git://github.com/mhennemeyer/prag_dave_testing.git
Matthias
Posted by: Matthias Hennemeyer | May 02, 2008 at 05:57 AM
A few more notes:
I had a long night hacking all the features together i would like
this framework to have.
While its still very experimental and not well tested I
can frankly say that it turned out as really nice.
(got it? git clone git://github.com/mhennemeyer/prag_dave_testing.git)
This are the features:
1. Upgraded transactional behavior:
testing "Transactional db testing" do
Model.destroy_all
testing "Create a model" do
Model.create
expect(Model.count) == 1
end
expect(Model.count) == 0
end
(This works only with sqlite3 and if the test db file
is named db/test.sqlite3)
2. Macro style testing block reuse:
testing "macro" do
@var = 1
testing "var should eql 1" do
expect(@var) == 1
end
# the testing block can be reused:
# 'expect_' + testing block name joined with underscores
expect_var_should_eql_1
end
This macro thing, together with the transactional behavior
lets you evaluate a testing block in different environments.
The environments will not be touched by the testing code:
testing "macros should behave transactional too" do
@var = 1
testing "change var to 5" do
@var = 5
end
expect_change_var_to_5
expect(@var) == 1
end
3. Rails Integration Testing:
integration_test do
testing "I can use the Integration::Session methods" do
post "/models", 'model' => {}
expect(@resonse.response_code)
end
end
What do you think?
Matthias
Posted by: Matthias Hennemeyer | May 04, 2008 at 08:49 AM