Copy
You're reading the Speedshop newsletter on Ruby and Rails performance by me, Nate Berkopec.

I have a couple of old-fogey Ruby opinions. Using Minitest is one of them.

Hello Rubyists,

There's always been a "prime stack" in Rails. The prime stack is the stack you get after you flip all the defaults to something else, and it's been called the prime stack because pretty much everyone swaps to the same things. Switching the test framework to RSpec is one of the most popular of these changes. At least 80% of Rails apps in the wild use RSpec instead of Minitest.

I want to explain why I switched to Minitest very early on in my career and never looked back and why I still think it's a superior choice for new applications.

What made me switch all those years ago was that I simply couldn't understand RSpec. For example, how would you explain what is happening here?

describe Factorial do
  # Position A
  it "does something" do
    # Position B
  end
end


What does `describe` _actually do_, as in, how is it implemented? What methods are available in Position A? What about Position B? What does it do? Are we defining new classes? What's available in any of these scopes?

What I find is that most people that use RSpec ignore these questions and instead treat RSpec the way it's intended to be used: as a DSL. You're not supposed to understand the Ruby going on underneath. That is by design. Instead, RSpec is a testing language that you have to learn.

I don't like this. This feels like magical incantations to me. Don't think too hard, just put your "its" inside of your "describes" and not the other way around and you'll be fine.

This is different from Rails itself. Rails is sometimes called "magical" but it's rarely magical in its usage of Ruby. Things are pretty straightforward from a Ruby standpoint. You subclass ActiveRecord::Base, you get all of its methods. That makes sense. Sometimes you include some modules. When implementing a Rails app, there's rarely a lot of metaprogramming underneath. Rails is a framework, in that sense, not a DSL.

Sometimes, a DSL is appropriate. But when it comes to testing, I don't think it is.

Testing is fundamentally about testing your understanding of how your program works. You write what you think the program does, and then you run the test and see if you were right.

In a testing DSL, the details of the setup and the comparison operation are obscured to you. Do you actually have any idea how expect(value).to eq(other_value) works in RSpec? What Ruby is actually being run to make that happen? I have no idea.

In Minitest, this question is very easy to answer. Allow me to explain Minitest's internals in 103 words:

1. Define a class that inherits from Minitest::Test. Thanks to the inherited hook, Minitest adds this class to a list of "test classes to run".
2. Define methods on that class that start with "test_". This adds those methods to the list of "methods to run".
3. Inside a test method, assert something. Assertions either return true or raise a specific type of exception if something wasn't as expected.
4. require minitest/autorun, which calls Minitest.autorun, which calls Minitest.run at process exit.
5. At the end of the run, tally up the (caught) exceptions and print the results.

That's it. When you write assert my_apps_method, there's no doubt about what Ruby code is being executed here or in what context. I mean, assert is like 7 lines long! It's so simple to understand! There is no magic. It's just really simple Ruby. It also means you can refactor and extend your test suite the same way you write Ruby. There's nothing more to learn or understand. Use modules, inheritance, even metaprogramming - all the tools you're used to work exactly the way you expect.

Another reason I like Minitest a lot is because of a question I get all of the time: is my app thread-safe? Well, the answer is probably, but I don't know and I can't prove it.

How do we prove things in our programs? With. Tests.

So, does it matter that your test suite runs in a single thread but your application in production runs in a more complicated, threaded environment? I think it does! Most Rails applications are running untested behavior in production, and it happens to be the kind of behavior that is the hardest to debug: threading!

RSpec will probably never be able to run in multiple threads due to the sheer extent of metaprogramming going on under the hood. Don't count on it any time soon, if ever.

My question is: are you okay with this? Are you okay with running critical behavior in production without tests?

Minitest solves this problem for us. Any test class in Minitest can be run multithreaded by using the parallelize_me! method. Of course, this also makes your tests a little bit faster, but that's not the real reason I love multithreaded testing: threading correctness!

Finally, if you're still skeptical about Minitest, it has spec syntax. To me, this feels quite a bit like RSpec anyway. Minitest's spec syntax implementation is just 300 lines (Minitest itself is 1500). You don't even have to give up your beloved "English-language-style" syntax!

Minitest is simpler, lets us test more important behavior, and you can still keep spec syntax if you want it. Don't forget about it.

-Nate
 
You can share this email with this permalink: https://mailchi.mp/railsspeed/why-i-prefer-minitest?e=[UNIQID]

Copyright © 2020 Nate Berkopec, All rights reserved.


Want to change how you receive these emails?
You can update your preferences or unsubscribe from this list.

Email Marketing Powered by Mailchimp