The goal behind minitest can be summed up in one word: simple. Minitest gets out of your way and allows you to write expressive and readable tests. These ideals are great, but sometimes we want a sprinkling of magic atop our tests; that’s what maxitest delivers. Maxitest adds features like context blocks and running by line number which make your test suite a little bit nicer. But there’s more to it than that. Let’s dive in!
While I will mention maxitest as our “test suite”, it’s important to keep in mind that minitest is the real power behind our testing. Maxitest is simply a series of add-ons to minitest that make it easier on the eyes. 65 Mustang Battery Tray
Rails is perfect for showing off maxitest in all its glory as we can encounter all forms of testing. For our purposes, we are going to make a news app with user-generated content. Throughout this article, we will use test-driven development to utilize maxitest’s major features and design our application.
Let’s get some rails new action going:
Now add maxitest and minitest-spec-rails to your Gemfile.
The minitest-spec-rails gem takes the pain out of integrating Minitest::Spec with Rails by extending Minitest::Spec into Rails’ own test case classes. You can read more on the readme.
First, let’s take a look at our test_helper.rb and add a require for maxitest. If you’re adding this to an existing code base, you just have to change the mini to maxi:
Now let’s make something to test. For the basics, just create a user model with two attributes: first_name and last_name:
Using TDD, we’re going to concatenate these two attributes into a full name. So, being the dogmatic test-first people that we are (or at least wish we were), let’s write a test!
A test file should already be generated in test/models/user_test.rb.
The class UserTest inherits from ActiveSupport::TestCase, which already has Minitest::Spec::DSL extended into it by the minitest-spec-rails gem. We then use a describe to describe what we’re testing. We use an it block to describe one of this method’s behaviors. Inside, the test creates an instance of User and then ensures that #name indeed returns the concatenation of first_name and last_name.
Let’s run the test suite:
Yay! Red. Now let’s actually create the method to get green.
To run the test this time, we’re going to copy and paste the code snippet maxitest gave to us to run this test again (on a line-by-line basis).
Great! As you can see, maxitest adds a satisfying green color to successful tests. This feature allows for a true red – green – refactor process. Speaking of which, let’s refactor our code a bit. I’ve decided that string interpolation might not be the best way to write our code (this is hypothetical; I have no preference), so I’m going to use #join on the two strings:
Let’s run our tests to make sure we didn’t break anything… ($ rake test) We didn’t.
Now we’re going to refactor our tests a bit and show off maxitest’s let_all method. In order to understand let_all, it’s important to understand what let itself does. Create an accessor method that is assigned to the block you pass it. It’s no different than defining a method inside of a describe block. let_all is an extension of let‘s functionality, caching the response of the passed block. This essentially creates a cached global variable for your block.
Notice in user_test.rb that we setup the it block using a variable assignment. We can refactor this into a let_all block inside of the describe #name block. This is practical because all it blocks inside of the describe #name block can rely on the same setup data and thus run faster.
Our new, refactored user_test.rb:
On the other side of the spectrum, maxitest adds the let! method. let! ensures that the block passed to it is always evaluated on each usage of the method.
As an aside from the current code, let’s look at one of maxitest’s more eccentric features: implicit subjects. I highly suggest against using this. Ever. “Implicit subject” is a synonym for a Mystery Guest, which makes code far less maintainable and useful. While an implicit subject might be useful to you right now by saving a few seconds, when you or another developer adds a test-breaking feature into the code base five months from now and you can’t figure out what the test is verifying, you’ll be sorry.
Due to this, I cannot in good conscience demonstrate maxitest’s implicit subject feature. The author himself did not include implicit subjects by default. They are in their very nature extremely hacky.
One thing I really wish minitest had is context blocks. Functionally, they are no different than describe blocks, but semantically they are. Sometimes it just makes more sense to write part of a test (especially acceptance tests) using context.
Maxitest made my wish come true in one line:
Context has no more functionality than describe, but it allows for more expressive and simple tests. To demonstrate the use of context blocks, we’re going to write an acceptance test for the listing of posts:
A great thing about the test-driven development methodology is its design aspect. While TDD is great for preventing regression, it also allows you to design your software before writing it. Acceptance tests are a higher-level test that allow you to design the features of your website before making it. With this in mind, let’s write a test for the main feature of our app: the news feed.
In our application we have users who have the ability to post text. In the body of our app we have a thread of these posts. We’re not going to add in anything complex, just a simple feed feature.
To build this right, we’re going to need a Post model that belongs to a user. Let’s generate that:
While we’re at it, let’s add some validations and associations to our posts and users:
Now we need to make some fixture data for our users and posts. Rails automatically generates samples fixtures in test/fixture, but we need to customize them a bit. Here’s some fixture data I made up, feel free to change it to whatever you would like:
These YAML files will generate fixtures for our tests accessible through model_plural_name(:fixture_name). Now that we have fixtures, let’s setup our acceptance testing environment. Rails does not come with a test case class for acceptance testing, so we’ll have to make one ourselves:
We add Capybara to the suite here, so make sure to add gem 'capybara' to your Gemfile.
The class we created (AcceptanceCase) gives us fixtures, route helpers, Rack-Test helpers, and Capybara.
Now let’s write a test to begin designing our feed feature.
Let’s run the suite now and see what happens:
It looks like we don’t have a route. Let’s make one. In order to have a route, we need a controller, so let’s generate a posts controller with an #index action:
This generator also makes a posts controller test. Let’s add a test for the route there as well:
Let’s run the suite again to see the new errors:
We have seen the first error before, but the second one is new; it’s from our posts controller test.
Now that we have test coverage and thus a goal of what needs to be implemented, we can write a few lines of code to make them pass.
4 lines of code later and we have 2 new passing tests! Obviously, we don’t have anything right now other than a working route and a static page, but it gives us a stable framework to build off of. Now we can build real functionality. Let’s head back to feed_test.rb.
Here use a context block to better describe a new feature in different contexts. For our purposes, there are two contexts: with posts and without.
That’s a lot of code, but it’s fairly easy to understand; it reads like English. Let’s just run the tests and see if they’re descriptive enough on their own without explanation:
Let’s look at the first failure: Immediately I know that a post’s (#113629430 to be specific) body is not being displayed within the post feed. Let’s fix this then! I see this list as a feature guide. It gives me incremental steps to completing a feature; which is great! We’re only going to fix the first test first, then we’ll fix the rest.
That’s it! Now let’s try the test… (mtest test/acceptance/feed_test.rb:26 -I test) Success! That was pretty easy, let’s fix the rest. Testing is great for allowing incremental feature building. To fix the next tests, all we have to do is look at one error, fix it, then fix the next one.
Here’s the final product!
Obviously, this is a nowhere near complete feature, but we get the core of TDD with great additions from maxitest.
I merely skimmed the surface of a vast philosophy and a great tool. Minitest with maxitest make a potent, yet lightweight combination for implementing test driven development into your Ruby applications. Next time you’re starting a new project, think twice about RSpec and consider giving minitest and maxitest a try.
Interested in more testing topics? I’d love to hear them! Feel free to leave a comment below.
Jesse Herrick is an avid Ruby developer who specializes in web development. He is a back-end developer at Littlelines and loves programming. You can read his personal blog at: https://jesse.codes.
Battery Cover Plastic This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.