Neospec
What is it?
Neospec is a testing library I’ve built for both Ruby and MRuby. I’ve tried some more experimental things with this. It’s also been a great learning experience.
What does it look like?
require "neospec"
unit = Neospec::Suite.new
neospec = Neospec.new(suites: [unit])
unit.describe "An example test" do
Given "apples are ripe" do
@apples = :ripe
end
And "bananas are ripe" do
@bananas = :ripe
end
Then "apples and bananas are as ripe as each other" do
expect(@apples).to_equal(@bananas)
end
But "the bananas become TOO ripe" do
@bananas = :over_ripe
end
Then "apples and bananas are now not as ripe as each other" do
expect(@apples).not_to_equal(@bananas)
end
end
neospec.run!
Why build this?
- It seemed fun
- I wanted something for Taylor
- I think a more modular approach makes more sens over RSpec/Minitest
Neospec breakdown
The top level Neospec
object has a Neospec::Logger
for printing out the
results of each Neospec::Spec
, I’m not completely convinced on this structure,
it feels like it should be on the Neospec::Suite
itself but I didn’t want to
have to define it for every Neospec::Suite
in a project and didn’t really see
much value in allowing it to be different for each Neospec::Suite
.
The Neospec::Reporter
concept comes from how Rspec
does many different
—format
flags. That always annoyed me and seemed counter intuitive, this way
how your specs are reported to both the end user and the continuous integration
system are defined in code rather than some dot file.
Neospec::Suite
is where it gets a bit more interesting, here we define what
sort of Neospec::Runner
we want, this is basically an execution strategy. The
only one right now is just sequential, but I see a future where threaded or
ractor based runners are used for unit tests and maybe integration. This is also
where all the hooks are defined. You’ve got your setup
and teardown
which
run before and after all the tests run but only once and you’ve got before
and
after
which run before and after each test.
I’ve always found lots of clutter in an RSpec
configuration block due to the
wide range of types of tests run within it. You end up doing all these tag
checks before every test, or superfluously running things like DatabaseCleaner
before unit tests. This completely separates those concerns and you’ll always
know exactly what specs will be run in that environment.
Neospec::Spec
contains quite a bit of interesting code, this will probably see
the most change and I’ll almost certainly end up with some sort of
Neospec::Spec::Context
to help prevent leaking internal state to the tests,
which I’ve had to rename my instance variables to make as hard as possible to do
accidentally. this is where you get all these lovely Given
, And
, Then
,
When
, etc blocks from. They’re just syntactic sugar for making readable tests
and are completely optional.
I have deliberately not allowed nesting specs. This has also made it easy to
write code that knows exactly how many tests there are without having to run
your whole suite. I also find you almost always end up with deeply nested tests
with a lot of shared state that is hard to keep track of. If you really want a
sort of “grouping” you could define a different Neospec::Suite
that does what
you want.
Should you use neospec?
No. Not unless you’re making a game in Taylor and even then I’d suggest holding off until I’ve actually used it myself for that!
Check it out anyway!
I suggest giving it a look anyway, it’s a fun little project and should be pretty easy to wrap your head around if you wanna look at the code.