This content originally appeared on DEV Community and was authored by Augusto Hubert
There is a version of this article in pt-BR, you can access it here
In recent years, much of my work has been helping teams expand and optimize their automated test suites. And one thing that comes up all the time is the same kind of problem: the tests exist, but they are hard to understand.
No one knows exactly what is being tested, where certain scenarios are, or whether the logical branches are truly covered. As a result, you see coverage gaps, redundant tests, and a lot of time wasted trying to understand what already exists.
On many occasions, when pairing with less experienced developers, I see a pattern repeat: they spend a long time scrolling up and down the test file, looking for code that looks “similar” to what they want to write. The idea is to place the new test “near the other similar ones.” This habit is understandable, but it is also a symptom of a structural problem: if you need to “guess” where the test should go, the file structure is not clear enough.
Over time, I developed a technique that helps me organize and visualize test structure. It is simple and practical, and it leverages a feature that almost every modern text editor has: code folding.
I call this technique Unfolding Tests.
The examples in this article use Ruby on Rails and RSpec, but the same principles apply to any other testing framework.
The idea behind Unfolding Tests
The idea is to use code block folding as a reading and writing tool. In VS Code, for example, you can fold and unfold blocks such as describe, context, and it.
To start, use the Fold All command (on macOS: Cmd + Shift + P; on Windows: Ctrl + Shift + P) and type “Fold All.”
This technique is grounded in the idea of reducing cognitive load. When everything is folded, you see only the skeleton of the test, which lets you quickly understand what is being tested and how the scenarios are organized, without reading implementation details.
That way, the focus becomes the INTENT rather than every line of code.
From there, I gradually expand the blocks and come to understand:
- What is being tested (
describe
) - What the possible scenarios are (
context
) - What the expectations are in each scenario (
it
) - And which setups are used at each level
This hierarchical view gives me a much clearer reading of the behavior of the system under test. What is most interesting is that the same process also works when writing tests.
When I start a new test file, I do not think in lines of code. I think in the structure I want to see when folded.
First I create the skeleton of the test:
RSpec.describe UserPolicy do
context 'when user is admin' do
it 'allows access'
end
context 'when user is not admin' do
it 'denies access'
end
end
When that file is fully folded, what I see is something like:
UserPolicy
when user is admin
allows access
when user is not admin
denies access
This is already enough to understand what is being tested and what the scenarios are.
By the way, this view is very similar to the RSpec output when you use the --format documentation option.
If you are not using that format yet, you should. It turns test execution into an almost narrative reading, showing exactly what each test describes, in the same hierarchy you defined in the describe, context, and it blocks.
After that, I unfold block by block and fill in the details: setups, mocks, expectations, and so on.
The hierarchy of setups
One of the most important parts of Unfolding Tests is respecting the hierarchy between the blocks and their setups.
Under each
describe
there should be a setup with the general prerequisites for all scenarios inside that block.Under each
context
there should be a setup with the prerequisites specific to that scenario. Your setup here must provide the conditions that make thecontext
description true.And the test code itself should always be inside the
it
block, which is the lowest level in the structure where expectations are verified.
describe 'Listing orders' do
let(:admin_user) { create(:user, :admin) }
before do
sign_in(admin_user)
visit orders_path
end
context 'when there are no orders' do
before { Order.delete_all }
it 'shows the empty state message' do
expect(page).to have_content('No orders found')
end
end
context 'when there are orders' do
let!(:orders) { create_list(:order, 2) }
it 'lists all orders' do
expect(page)
.to have_text(orders.first.id)
.and have_text(orders.second.id)
end
end
end
If you fold this file, even without seeing the implementation details, you can understand the purpose of the test, the scenarios it covers, and the expected behavior in each case. Then, when you unfold each block, you reveal the necessary details: first the setup, then the test.
Contexts as logical branches
An important principle of this technique is that each logical branch deserves its own context
. If a variable can take three states (for example full
, partial
, empty
), then you should have three contexts, one for each case.
Example:
context 'when report is full' do
# ...
end
context 'when report is partial' do
# ...
end
context 'when report is empty' do
# ...
end
This helps ensure that the tests truly explore all relevant possibilities.
And more: to me, it does not make sense to have a context without others that represent opposing states. If there is a context 'when user is admin'
, there should also be a context 'when user is not admin'
. These pairs make it clear that the test covers both sides of the logic, and that is easy to see when the file is folded.
Why this works
Unfolding Tests turns the act of reading and writing tests into something visual and incremental.
You see the structure before you see the details, which helps you:
- Quickly understand the scope of the file
- Identify redundancies and gaps
- Ensure that each logical path has its counterpart
- Write tests that also serve as living documentation
In the end, a good test is also a good form of documentation. And the clearer the intent expressed in the structure, the easier it will be for anyone to understand the expected behavior of the system.
Tips to start applying
- Enable code folding in your editor (VS Code, RubyMine, Sublime, etc.).
- Fold everything (Fold All) and check whether the structure makes sense.
- If you cannot understand what is being tested by looking only at the folded file, something is wrong.
- When writing new tests, think first about the folded structure.
- Use
context
to express each logical variation and always in pairs when it makes sense. - Create setups in the correct hierarchy: first in
describe
(general), then in eachcontext
(specific). - Only then fill in the contents of the
it
blocks.
Unfolding Tests is a simple way to bring clarity and intent to your tests.
It forces you to think about structure before details and to treat each scenario as part of a cohesive whole. In the end, the benefit is twofold: tests become easier to understand and the code is less likely to get lost among confusing or redundant cases.
If you often get lost in large test files, try folding everything and then unfolding gradually. It is a very simple technique, yet it is impressive how it changes the way you read and write tests.
This content originally appeared on DEV Community and was authored by Augusto Hubert

Augusto Hubert | Sciencx (2025-10-09T16:13:43+00:00) Organizing Tests with Clarity: the Unfolding Tests Technique. Retrieved from https://www.scien.cx/2025/10/09/organizing-tests-with-clarity-the-unfolding-tests-technique/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.