name: rspec description: MUST use this skill when writing, reviewing, or modifying Ruby RSpec tests (*_spec.rb) in Canvas
When writing or reviewing RSpec tests in Canvas, there are several guidelines to ensure tests are effective and maintainable. Canvas' codebase is quite old, so there are many legacy tests that don't follow modern best practices.
- Prefer
letandlet!for defining test data instead of instance variables. This provides better scoping and lazy loading of test data. This also applies to usingletinstead ofbeforeblocks for setting up test data. - When an ActiveRecord model instance is shared across multiple tests without any stubbed methods, use
let_onceto define it. This will create the object once and reuse it across examples, improving test performance. - When using
subject, remember that it's a noun -- don't use it as a way to perform a common action across multiple examples. Thesubjectshould be the object under test. If you want to perform a common action across multiple examples, write a method. You can call that in each action. Or use abeforeblock if it makes sense. - Look for repetitive patterns in your tests, and extract them into
let,beforeblocks, helper methods, or shared context as appropriate. This will make your tests more DRY and easier to read. - Don't create "factory" methods for models unless the method is doing something non-trivial. Most factory methods are simply guessing a couple of defaults (that you're likely passing in anyway) and then calling
create!on the model. Simply pass your attributes directly tocreate!yourself. If you find yourself wanting to infer the same values over and over, consider abefore_validationhook on the model that can infer them for you. - Don't use raw
doubleunless absolutely necessary.instance_doubleandclass_doubleprovide better guarantees that your test doubles are accurate representations of the real objects. - For specs testing individual methods, group all examples for that method within a
describe "#method_name"ordescribe ".class_method_name"block. - For controller specs and integration specs testing a single action, group all examples for that action within a
describe "HTTP_METHOD action_name"block (e.g.describe "GET index"). - Do not create any classes or modules within spec files. If you need to define a class or module for testing purposes, define it dynamically (
Class.new), and assign it either withletorstub_const. - Do not create any top-level methods within spec files. Helper methods can be defined in the appropriate example group, or in a shared group that can be included where needed.
- Use the strictest equality matcher that makes sense:
beasserts object identity -- use it for numbers, symbols, true, false, nil, or when you specifically want to assert that two variables point to the same object.eqlasserts that two values are not just semantically the same, but also of the same type without conversions -- this should be the most commonly used matcher for types like strings, arrays, hashes, and custom objects.eqasserts that two values are semantically the same, allowing for type conversions.