Skip to content

Conversation

@NicolasMassart
Copy link
Contributor

@NicolasMassart NicolasMassart commented Mar 7, 2025

Test Naming Guidelines Update

Updates our test naming conventions to improve clarity and maintainability of our test suite.

Key Changes

  • Established clear guidelines for naming unit tests
  • Added examples of good and bad practices
  • Introduced principles for more descriptive and concise test names

Note

Overhauls unit testing guidelines to tighten naming/structure, isolation, mocking, and snapshot practices with clear examples.

  • Docs (docs/testing/unit-testing.md):
    • Clarifies Jest usage and colocating test files.
    • Expands describe guidance (by function and scenario) and introduces stricter it naming conventions (active verbs, no “should”) with do/don’t examples.
    • Emphasizes focused tests; discourages compound expectations and vague names.
    • Advises testing behavior via public APIs (not private code) with illustrative examples.
    • Highlights four-phase tests and encourages visual separation of phases.
    • Strengthens test isolation practices: reset/restore mocks, handle globals safely, prefer dependency injection, and use factories over shared state.
    • Recommends setup helpers instead of beforeEach, including wrapper patterns for setup/teardown.
    • Keeps critical test data local to tests for readability.
    • Prefers Jest mocks over Sinon and cautions on general manual mocks.
    • Refines snapshot testing guidance and naming (e.g., “matches rendered snapshot”).

Written by Cursor Bugbot for commit 39208d1. This will update automatically on new commits. Configure here.

@NicolasMassart NicolasMassart added the enhancement New feature or request label Mar 7, 2025
@NicolasMassart NicolasMassart self-assigned this Mar 7, 2025
@NicolasMassart NicolasMassart requested a review from a team as a code owner March 7, 2025 18:19
@NicolasMassart NicolasMassart moved this to Needs dev review in PR review queue Mar 7, 2025
@NicolasMassart NicolasMassart changed the title update unit test naming guidelines Update unit test naming guidelines Mar 7, 2025
@NicolasMassart NicolasMassart requested a review from Copilot March 11, 2025 13:27
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR updates our unit test naming guidelines to improve clarity and maintainability of our test suite.

  • Added a structured set of instructions for naming tests using active verbs.
  • Included new examples of both poor ("Don't") and good ("Do") naming practices.
  • Provided an extended list of considerations to ensure test names are descriptive and concise.
Comments suppressed due to low confidence (1)

docs/testing/unit-testing.md:125

  • [nitpick] The test name in this example includes the 'should' prefix even though the guidelines recommend avoiding it. Consider revising the test name to remove the 'should' prefix and focus directly on the behavior.
it('should successfully add token when address is valid and decimals are set and symbol exists', () => {

Rsed19901

This comment was marked as spam.

avoid confusion as when I asked to use the 3rd person form for verb, this doesn't apply to "render" in "render matches snapshot" as render is not the verb here but a noun. The verb is match which is correct here as "matches".
So they replaced the correct phrase by "renders matches snapshot" which is wrong.
Remove render to avoid verb/noun confusion and anyway, render is obvious and part of the test implementation
@hieu-w hieu-w self-requested a review June 11, 2025 11:21
@NicolasMassart NicolasMassart enabled auto-merge (squash) October 10, 2025 12:12
…ions

- Break up test naming examples into pairs with explanations
- Add example for missing/unclear test descriptions
- Clarify error-related test naming (be specific about error types)
- Update snapshot test naming to 'matches rendered snapshot'
- Remove vague terms like 'gracefully' and 'handles' from examples
- Enhance guidance to avoid weasel words and subjective language
@NicolasMassart NicolasMassart requested a review from hieu-w October 17, 2025 10:52

This comment was marked as off-topic.

@NicolasMassart NicolasMassart enabled auto-merge (squash) October 17, 2025 10:52
Copy link
Contributor

@mcmire mcmire left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry this has been open for so long, I just realized I already reviewed this a while back but I forgot about this 🤦🏻

Thank you for this! As the original author of this guide, I fully admit I have a habit of using too many words. So I appreciate these changes. I had some comments/suggestions below, but they are all minor.

## 💡 Use `describe` to group tests under scenarios

If you have many tests that verify the behavior of a piece of code under a particular condition, you might find it helpful to wrap them with a `describe` block so you only need to specify that condition once. Use a phrase starting with `if ...` or `when ...` so as to form a sentence when combined with the test description.
When multiple tests verify behavior under the same condition, wrap them in a `describe` block. This way, you specify the condition only once. Start the description with `if ...` or `when ...` to form a complete sentence with each test description.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps this?

Suggested change
When multiple tests verify behavior under the same condition, wrap them in a `describe` block. This way, you specify the condition only once. Start the description with `if ...` or `when ...` to form a complete sentence with each test description.
When multiple tests verify behavior which falls under the same condition, wrap them in a `describe` block. This way, you specify the condition only once. Start the description with `if ...` or `when ...` to form a complete sentence with each test description.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made this suggestion because I felt like the new wording could be misinterpreted to mean that the condition applies to the test. But it applies to the behavior being tested.

However, I also notice that the old wording can be misinterpreted this way as well.

In addition, the second sentence isn't completely clear (although again, the original text wasn't clear, either).

Maybe something like this is still understandable?

Suggested change
When multiple tests verify behavior under the same condition, wrap them in a `describe` block. This way, you specify the condition only once. Start the description with `if ...` or `when ...` to form a complete sentence with each test description.
When multiple tests verify conditional behavior, group them together in a `describe` block. This way, you only need to specify the condition once. Start each test's name with `if ...` or `when ...` to form a complete sentence when combined with the `describe`.

- **Makes writing new tests difficult.** The `beforeEach` setup may not fit new test scenarios. You may need complex refactoring to remove steps that don't apply, or use workarounds that hurt consistency and readability.

Setup helper functions serve the same purpose as hooks without causing these problems. In this way, tests that need setup data for specific scenarios can specify them easily by passing options to the function, which communicates their importance to readers over other kinds of setup data in the context of the test. Plus, managing setup code becomes easier, as it can be refactored more easily if necessary.
Setup helper functions solve these problems. Tests can pass options to the function to specify the setup they need. This shows readers what is important for each test. Setup code is also easier to refactor when needed.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like some of the original wording here:

Suggested change
Setup helper functions solve these problems. Tests can pass options to the function to specify the setup they need. This shows readers what is important for each test. Setup code is also easier to refactor when needed.
Helper functions allow tests to be set up without creating these problems. Tests can pass options to the function to specify the setup they need, showing readers what is important for each test and making it easier to refactor when needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not "solve"?
The guideline is not expected to be a novel with literary qualities or legal text.
It should be short, concise, precise yet easy to understand for anyone whatever their native language and english skills. Also it makes it easier for automated translator and AI.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. I understand this is reference documentation, and if we can use simple and concise language, the better. I like to read well-written documentation, so I am probably biased in that regard. I also understand that we want to make this text easy to read for non-native English speakers, so I appreciate the feedback on how we can achieve that. Maybe we can find a middle ground.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anyway, I was mainly looking for these sentences to sound a little more natural. What about this:

Suggested change
Setup helper functions solve these problems. Tests can pass options to the function to specify the setup they need. This shows readers what is important for each test. Setup code is also easier to refactor when needed.
Setup helper functions solve these problems in two ways:
- Tests can pass options to the function to specify the setup they need, showing readers what is important for each test.
- Setup code is easier to refactor when needed.

Setup helper functions solve these problems. Tests can pass options to the function to specify the setup they need. This shows readers what is important for each test. Setup code is also easier to refactor when needed.

The functional pattern presented above can not only be applied to setup code but also teardown code. In that case, your helper becomes a wrapper by taking another function:
You can apply this pattern to teardown code too. In this case, your helper function wraps another function:
Copy link
Contributor

@mcmire mcmire Nov 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Taking your suggestion and avoiding the word "this" so it isn't ambiguous:

Suggested change
You can apply this pattern to teardown code too. In this case, your helper function wraps another function:
Helper functions can not only be used for setup, but also for teardown.
Consider the following example, which uses `beforeEach` to create a controller and then `afterEach` to destroy it:

});
```

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Here's how we could improve this:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure it adds more meaning than the checkmark

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough. How about this?

Suggested change
A function that wraps the main part of the test, automatically creating and destroying the controller before and after calling the function, is a more maintainable pattern:

Let me know if it's too many words :)

Copy link
Contributor

@mcmire mcmire left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the discussion @NicolasMassart. Again I appreciate your attempts to make this more understandable and thanks for your patience. I think we are close on this.

## Keep tests isolated

A test must pass whether it is run individually or whether it is run alongside other tests (in any order).
A test must pass when running alone or with other tests (in any order).
Copy link
Contributor

@mcmire mcmire Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"a test ... when running alone" sounds like the test is running itself, hence the suggestion.

I see what you mean about "whether". What do you think about this instead:

Suggested change
A test must pass when running alone or with other tests (in any order).
A test must always pass. Running a test by itself or with a group of other tests must not change the result.

Tests must run in a clean environment. If a test changes any part of the environment, it must undo those changes before finishing. This prevents the test from affecting other tests.

Here are ways to do this:
Ways to keep tests isolated:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, good callout. Perhaps we could stick a "The" at the front then?

Suggested change
Ways to keep tests isolated:
The next sections suggest ways to keep tests isolated.

Global variables are properties of the global context (usually `global`). Changes to these variables affect every test in a test file.

If the global you want to change is a function, it is best to mock the function using `jest.spyOn` so that it is easy to undo it later (note: this guideline is best paired with the above guideline so that you can make this happen automatically):
If the global is a function, mock it using `jest.spyOn`. This makes it easy to undo later. (Combine this with the previous guideline to handle it automatically.)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To provide more details on this suggestion:

  • It seems that we are telling the reader about a concept and then directing them to follow some instructions. As a reader the transition was not as smooth as I had hoped. By using the word "you" I thought this paragraph flowed a bit more smoothly with the previous one.
  • We say "this makes it easy to undo later", but is the "it" in this sentence meant to refer to the mock function created or the global? My thought is that the thing we are undoing is the mock function. But if you disagree (or the wording you suggested is understandable well enough) we can keep the language as-is.
  • I don't expect people to read this section all the way through so I thought we could remove "(Combine this with...)".

```

Now say the global you want to change is not a function:
If the global is not a function:
Copy link
Contributor

@mcmire mcmire Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made this suggestion because I wanted to start this sentence with "If you want to..." in order to match the suggestion I made above. It also felt like we were missing instructions for the reader.

**Do not test private code directly.** Instead, test the public methods that call the private code. Write tests as if the private code is part of the public method.

Test code marked as private as though it were directly part of the public interface where it is executed. For instance, although you might write the following:
For example, you might write this code:
Copy link
Contributor

@mcmire mcmire Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made this suggestion because it pairs with the next suggestion. Together it says:

"Take this code for example: . This code behaves exactly the same as though ..."

But if you see these sentences as separate, here is another attempt:

Suggested change
For example, you might write this code:
The following example defines a couple of private methods:

```

this is what consumers see:
Consumers (and your tests) can only see the public interface, which behaves as if the private methods were inlined:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made this suggestion to pair with the previous suggestion (I am assuming that the reader has read the example above). But if you don't like the use of "this" (which I admit could be ambiguous), or if you think this sentence should stand alone, then here is another attempt:

Suggested change
Consumers (and your tests) can only see the public interface, which behaves as if the private methods were inlined:
Since consumers (and tests) can only see the public interface, the example above behaves as if private methods and properties were inlined:

```

and as a result, this is how the method ought to be tested:
Therefore, test the public `stop` method by verifying all the behaviors that result from calling it, including the effects of the private methods:
Copy link
Contributor

@mcmire mcmire Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another attempt, which is simpler:

Suggested change
Therefore, test the public `stop` method by verifying all the behaviors that result from calling it, including the effects of the private methods:
A test suite for this class might look like the following:

### Restore function mocks after each test

Set Jest's [`resetMocks`](https://jestjs.io/docs/configuration#resetmocks-boolean) and [`restoreMocks`](https://jestjs.io/docs/configuration#restoremocks-boolean) configuration options to `true`. This instructs Jest to reset the state of all mock functions and return them to their original implementations after each test. (This option is set in MetaMask's [module template](https://github.com/MetaMask/metamask-module-template).)
Set Jest's [`resetMocks`](https://jestjs.io/docs/configuration#resetmocks-boolean) and [`restoreMocks`](https://jestjs.io/docs/configuration#restoremocks-boolean) configuration options to `true`. Jest will then reset all mock functions and return them to their original implementations after each test. (MetaMask's [module template](https://github.com/MetaMask/metamask-module-template) includes this setting.)
Copy link
Contributor

@mcmire mcmire Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made this suggestion because I thought it was shorter. I also thought we didn't need to mention the module template; it is enough to follow these instructions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

Status: Needs dev review

Development

Successfully merging this pull request may close these issues.

6 participants