Context
Until recently, aside from code autocomplete capabilities, I had yet to see a truly reliable way to generate AI-powered code that would be useful for my company, Linkana.
I agree with DHH when he says that AI is merely an excellent junior developer—sometimes producing overly complex code or introducing subtle, hard-to-spot bugs. Whether this will change in the future is uncertain. What matters is that today, by providing good context, we can get AI to help us write high-quality code.
Cursor AI
In September of last year, Rafael França (Rails Core) spent a few days in São Paulo and showed me that he was using Cursor instead of VSCode. I decided to switch to Cursor AI, and since then, I’ve been taking advantage of its superior autocomplete capabilities and its ability to explain code snippets. So far, nothing too sophisticated.
Cursor Rules
Over the past few weeks, I discovered the Rules for AI feature—a way to teach AI how to "think." This allows us to guide Cursor to behave like a good junior developer would: recognizing code patterns, replicating them in similar contexts, and ensuring consistency across the project.
Migrating from RSpec to MiniTest
Last year, we decided to migrate to MiniTest. We started writing new tests in MiniTest, but we still have hundreds of files written in RSpec. As expected, no one wants to prioritize rewriting tests, making this migration extremely slow.
To speed up the process, we created a structure like this, allowing us to define specific rules for different contexts within our code:
.cursor/rules/
├── rails8.mdc
├── models/
│ ├── active_record.mdc
│ ├── tests.mdc
│ └── postgresql.mdc
├── controllers/
│ ├── api.mdc
│ └── tests.mdc
└── views/
├── phlex.mdc
└── components.mdc
Let's take a look at models/tests.mdc
:
---
description: ""
globs: test/models/**/*.rb
alwaysApply: true
---
# Rails Model Testing with Minitest and FactoryBot
## General Structure
- Each model should have its own corresponding test file (e.g., `user_test.rb` for the `User` model)
- Test files should be organized within the `test/models` directory, mirroring the application structure
- Subdirectories can be used to organize specific components (e.g., `test/models/documents/`)
## Test Configuration and Organization
- Each file should include `require "test_helper"` at the beginning
- The test class should inherit from `ActiveSupport::TestCase`
- Tests should be written using `test "description" do` instead of `def test_description`
- Related tests should be grouped together
- The `setup` method should be used to create test data with factories
## Using Factories
- Factories should be defined in `test/factories/`
- Use `create(:factory_name)` for persisted records and `build(:factory_name)` for non-persisted ones
- Use `build_stubbed(:factory_name)` when persistence is not needed
- Use descriptive traits for factory variations (e.g., `:with_buyer`, `:not_clear`)
## Example Structure
# frozen_string_literal: true
require "test_helper"
class ModelNameTest < ActiveSupport::TestCase
def setup
@model = create(:model_name, :with_trait)
end
test "associations" do
assert_respond_to(@model, :association_name)
# Test other associations...
end
test "validations" do
invalid_model = build(:model_name, required_attribute: nil)
refute(invalid_model.valid?)
assert_equal(invalid_model.errors[:required_attribute], ["expected error message"])
end
end
## Style and Best Practices
- Tests should be independent and isolated
- Avoid fixtures whenever possible, preferring factories
- Use meaningful test data to clearly express intent
- Test edge cases and failure scenarios
- Follow the "Arrange, Act, Assert" pattern
- Cover both positive and negative cases
This rule establishes standards for things like using factories, organizing tests, and formatting test "instance_method" do
statements. Beyond guiding AI in writing tests, these rules help maintain project standardization and serve as documentation of best practices for both the human team and the AI itself.
The most interesting part is that I generated this rule with a single prompt in Cursor. I provided documentation and examples from my own code, and the AI automatically created the instructions—I only had to make minor adjustments.
Using It in Practice
With the rules in place, it was time to test them in practice. When I asked Cursor to migrate an RSpec test to MiniTest, it automatically identified the applicable rule based on globs: test/models/**/*.rb
.
To validate the process, I migrated the files one by one. The only error I encountered was related to language—Cursor expected error messages in English, but my application is in Portuguese.
In the end, I was able to open a Pull Request with minimal effort and the expected quality. Will this PR be merged without any requested changes?
Top comments (0)