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.