On Saturday we started the TDD flow in an Eventide component, and we had all of the excitement of… laying out the folder structure. I’m not sure how far we’ll get today. I’ll write what seems like 2 minutes of reading, and then we’ll pick it up again tomorrow.
Here’s a refresher link to the repo, and we’re in the test/automated/handle_commands/build/initiated.rb
file. First, we need access to test/automated/automated_init.rb
, and then we’ll set up the basic structure of the test. We’re using TestBench to write these tests, and diving deep into it will have to wait for another email. It provides us context
blocks, and you’ll see how those blocks nest within one another in the output:
require_relative "../../automated_init"
context "Handle commands" do
context "Build" do
context "Initiated" do
end
end
end
TestBench test files are Just Ruby Programs™, so you can run them like you would any other Ruby program with ruby test/automated/handle_commands/build/initiated.rb
:
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [arm64-darwin21]
TEST_BENCH_DETAIL: nil
Handle Commands
Build
Initiated
Our context names match the folder structure within test/automated
, and this is useful for understanding where a test is located. Having run the test at this point, we’ve verified that we’ve setup our contexts correctly, and that we’ve found our link to the rest of the project (automated_init.rb
) correctly.
Now then, let’s write a test.
This sparks joy
Here is one of my favorite things about Eventide. We’re going to write a test for a message handler class. The class in question is going to live at lib/composite_component/handlers/commands.rb
. Handlers in Eventide components are analogous to controllers in an MVC framework. Unlike in an MVC framework, watch this (omitting some of the lines for space):
context "Initiated" do
handler = Handlers::Commands.new
end
Did you catch it? We just instantiate an instance of the handler class and then call methods on it. If you find you have too much peace and goodwill towards mankind in your heart, try doing that with a controller in the predominant web framework in Ruby, and you’ll be cured of that in a jiffy.
Let’s try running the test again:
> ruby test/automated/handle_commands/build/initiated.rb
TEST_BENCH_DETAIL: nil
Handle Commands
Build
Initiated
test/automated/handle_commands/build/initiated.rb:6:in `block (3 levels) in <main>': uninitialized constant Handlers (NameError)
# A bunch more stack trace
The test is telling us that we don’t have something called Handlers
in our project yet. Thats fine. We can add it at lib/composite_component/handlers/commands.rb
:
module CompositeComponent
module Handlers
class Commands
end
end
end
We’re within the CompositeComponent
and then we add a module
and class
named Handlers
and Commands
, respectively. We don’t have to name them that, but that’s the convention. This is the class that handles commands belonging to the composite-component
.
One last step, we need to add this to our component loader file which in our case you’ll find at lib/composite_component.rb
. It’s a sibling to the composite_component
folder that all our component code is in. We don’t do auto-loaders in Eventide because we don’t hate ourselves, so each time you add a file to the project, you’ll add a line in here. Order matters somewhat, so be sure to reference the completed file in the repo to see how to order unfolds. I don’t want to put that much duplication in our emails.
Anyway, the line we’re adding is:
require "composite_component/handlers/commands"
When we require automated_init.rb
into our program at the top of the test file, we follow a chain that leads to the load_path.rb
file in the root of the repository. This sets it up so that when we require
a file, it looks for it off of lib
.
One of the tenets of TDD is that we write enough code to get our failing test to pass. Let’s see how we did:
$ ruby test/automated/handle_commands/build/initiated.rb
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [arm64-darwin21]
TEST_BENCH_DETAIL: nil
Handle Commands
Build
Initiated
We’re back to a passing state.
This email is getting long now, so let’s just recap what we learned:
- How to set up a test in TestBench
- When we test things in Eventide, we just instantiate them and call methods on them
- Some of the other file structure in an Eventide component
Tomorrow, or next time the email is about TDD, we’ll get into the notion of test controls, one of my favorite things I’ve learned about since using Eventide, although the concept is not Eventide-specific.