So far I’ve built a couple models that relate to each other, their controllers and routes, and a little React code. Before things become too unwieldy, I think now is a good time to create some tests for these guys. I’m going mostly off faith that the convention to test is generally a good thing. I can’t appreciate fully how much time and effort testing will save me, but I trust this method should drive my app’s development.
Following along with the Rail’s Guide for testing, let’s create a test that ensures our Narration model validates for the presence of a title. Since we created our app from scratch, we don’t have the generated folders & files. Let’s minimally create these one at a time. First up, the folder called
test. This folder will be nestled between
tmp for my project directory. Within
test, let’s create a folder for
models and the file
test_helper.rb, let’s add this code:
This helper file holds the default configuration for our tests. Now within the
models folder, let’s create our
narration_test.rb file. Therein type on the first line
require 'test_helper.rb'. You can leave off
.rb but I prefer being verbose at this stage. It’s very evident now that you’re requiring our testing configuration file. Next, create an inherited class from ActiveSupport::TestCase like so:
Let’s add some pseudo-code and run the test. Here’s my test file with a test code block added:
Before we run the test, the big three things are starting with
do-end block, and
asserting something that returns a Boolean. I could be wrong about this claim, but at present, this pattern rings true.
Back in our terminal, run
Running via Spring preloader in process 4209 Run options: --seed 34980 # Running: . Finished in 0.010372s, 96.4134 runs/s, 96.4134 assertions/s. 1 runs, 1 assertions, 0 failures, 0 errors, 0 skips
Well, that’s nifty. Let’s change our test to make it fail!
And in “typical Man City” fashion, the test passed, which is actually a failure for our case (because we expected it to fail). The only code I have in my Narration model is
belongs_to :narrator. When I comment this line out, it will fail. Here’s the passing test:
Running via Spring preloader in process 5664 Run options: --seed 32928 # Running: . Finished in 0.061844s, 16.1697 runs/s, 16.1697 assertions/s. 1 runs, 1 assertions, 0 failures, 0 errors, 0 skips
And the failing test:
Running via Spring preloader in process 6488 Run options: --seed 19291 # Running: F Failure: NarrationTest#test_should_not_save_narration_without_title [/Users/kelton/Sites/narify/test/models/narration_test.rb:5]: Expected true to be nil or false bin/rails test test/models/narration_test.rb:3 Finished in 0.045048s, 22.1985 runs/s, 22.1985 assertions/s. 1 runs, 1 assertions, 1 failures, 0 errors, 0 skips
So in this case, it’s actually not a very good test! So let’s jump over to Semaphore’s testing guide. Let’s define a “valid” Narration as one that has a title, published value (true or false), and a source_url. We’ll add the related Narrator later.
Ok, it seems like these tests could grow considerably for each valid/invalid check. I’ve commented out my
belongs_to :narrator line and got the following results when running
rails test test/models/narration_test.rb:
Running via Spring preloader in process 8647 Run options: --seed 24041 # Running: F Failure: NarrationTest#test_invalid_without_title [/Users/kelton/Sites/narify/test/models/narration_test.rb:15]: narration is valid without a title bin/rails test test/models/narration_test.rb:13 .F Failure: NarrationTest#test_invalid_without_published_status [/Users/kelton/Sites/narify/test/models/narration_test.rb:21]: narration is valid without a published status bin/rails test test/models/narration_test.rb:19 F Failure: NarrationTest#test_invalid_without_source_url [/Users/kelton/Sites/narify/test/models/narration_test.rb:27]: Expected true to not be truthy. bin/rails test test/models/narration_test.rb:25 Finished in 0.079035s, 50.6105 runs/s, 50.6105 assertions/s. 4 runs, 4 assertions, 3 failures, 0 errors, 0 skips
Writing these tests first, I can see how this output should annoy me. For example, “narration is valid without a title” makes me cringe, so now we need to include validations in our model file.
Now running our test in the terminal,
rails test test/models/narration_test.rb:
Running via Spring preloader in process 9112 Run options: --seed 12134 # Running: .... Finished in 0.044366s, 90.1591 runs/s, 157.7785 assertions/s. 4 runs, 7 assertions, 0 failures, 0 errors, 0 skips
In my mind, the test we just wrote only wraps around the Narration model definition. So “by definition”, our model should pass these “definition” tests. Before moving out of the definition, let’s address the unruliness by abstracting away some of the repetitive stuff. Let’s take the local variable
narration defined in our
valid test, and assign in to an instance variable in a
#setup method. Then we’ll use that instance variable in subsequent tests by setting the invalid-attribute-to-be to
assert_not_nil statements then just modify their
narration local variable to be the
@narration instance variable. Here’s my
narration_test.rb file with changes:
And I ran the test again and got zero failures and zero errors. But if we add more attributes to our Narration model, we’ll need to dip into this test (and possibly other tests that use this test data) to manage the growing model. Let’s abstract one step further using “fixtures”. A newly generated Rails app and model would have this folder/file handy for us. Let’s create it manually.
test/fixtures/narrations.yml. Note the new fixture folder and the plural narrations Yaml file. Let’s abstract now into this Yaml file:
And now only modify the
#setup method in our test file:
So we’re still going to be using the
@narration instance variable, but we’re assigning it a value that pulls (by convention) from the fixture file called
narrations with the argument
:valid defined therein. Tests pass still. That’s all for this first run at testing with Rails using the default Minitest, even though we never actually referenced “Minitest”. It’s “under the hood” as they say, right?