Skip to content

Commit 379c6e7

Browse files
committed
removed specific details about each test framework in order to allow agents to consult the frameworks specific files'
1 parent 7f6a149 commit 379c6e7

File tree

1 file changed

+0
-166
lines changed

1 file changed

+0
-166
lines changed

context/usage.md

Lines changed: 0 additions & 166 deletions
Original file line numberDiff line numberDiff line change
@@ -108,169 +108,3 @@ You are going to think about tests by referring to the level of software process
108108
1. Verifying individual components - you are going to be writing Unit Tests.
109109
2. Verifying interaction between multiple units - you are going to be writing Integration Tests.
110110
3. Verifying end-to-end-workflows - you are going to be writing System Tests.
111-
112-
Let's understand each of the categories:
113-
114-
#### Unit Tests
115-
116-
A unit test is a small, isolated check of a single `unit` of code - usually one function / method / class that needs to work exactly as intended. When writing unit tests you have to think about:
117-
1. Isolation - you replace or mock out any external dependencies, while still abiding by the general test-writing best practices.
118-
2. Speed - they need to be lightning-fast.
119-
3. Determinism - given the same inputs, a unit test always passes or fails - so no dependencies on time, timezones, network timeouts, etc.
120-
4. Scope - They focus on internal module logic.
121-
122-
##### Best Practices When Writing Unit Tests
123-
124-
1. Follow the **Arrange-Act-Assert (AAA) Pattern** to keep each test’s intent crystal-clear.
125-
- **Arrange**: set up inputs, mocks, or test data.
126-
- **Act**: invoke the method or function under test.
127-
- **Assert**: verify the expected outcome.
128-
2. **Write One Logical Assertion per Test** to keep it as isolated and deterministic as possible.
129-
3. **Use Descriptive, Consistent Naming** to improve maintainability and the happiness of the dev fixing the test.
130-
131-
```ruby
132-
# good
133-
test_calculateTotal_givenEmptyCart_returnsZero()
134-
test_parseDate_whenInvalidFormat_throwsParseException()
135-
136-
# bad
137-
test_calculateTotal1()
138-
test_parseDate2()
139-
```
140-
141-
4. **Mock-out any external dependencies** and try instantiating objects directly in memory when possible. In order to achieve this you would be required to add another gem dependency to your project's `Gemfile`. One such gem for Rails is `factory_bot_rails`.
142-
143-
Continuing with the Rails example:
144-
145-
###### Minitest
146-
147-
```ruby
148-
# in your test:
149-
class UserTest < ActiveSupport::TestCase
150-
def test_user_stuff
151-
user = build_stubbed(:user)
152-
assert user.persisted? # => true
153-
assert_equal 1, user.id # FactoryBot will assign a fake id
154-
# no DB calls were made
155-
end
156-
end
157-
```
158-
159-
###### RSpec
160-
161-
```ruby
162-
# in your spec:
163-
RSpec.describe User, type: :model do
164-
let(:user) { build_stubbed(:user) }
165-
166-
it "behaves correctly" do
167-
expect(user).to be_persisted
168-
expect(user.id).to be_a(Integer)
169-
# no SQL was executed
170-
end
171-
end
172-
```
173-
174-
###### Sus
175-
176-
```ruby
177-
# in your Sus test file:
178-
test "user is valid (stubbed)" do
179-
user = build_stubbed(:user)
180-
expect(user.persisted?).to be(true)
181-
end
182-
```
183-
184-
As an alternative to this, you can use an in-memory SQLite database by configuring your `database.yml` as such:
185-
186-
```yml
187-
# config/database.yml
188-
test:
189-
adapter: sqlite3
190-
database: ":memory:"
191-
```
192-
193-
5. **Use Parameterized Tests for Repeated Scenarios** to avoid boilerplate and ensure consistency when the same logic must be validated across multiple inputs, parameterize.
194-
6. **Group and Organize Tests Logically** by mirroring the production code's package/ module structure and by setting up fixtures for common initialization.
195-
7. **Do not add an abundance of comments around tests** as the names should be descriptive enough.
196-
197-
#### Integration Tests
198-
199-
Integration tests verify that multiple pieces of your system work together correctly. They live between fast isolated unit tests and slower end-to-end UI tests. They give you confidence that the modules communicate properly. When writing integration tests you have to think about:
200-
1. Scope - define the communication between which units will be tested (for example, querying an API endpoint that queries the database through the Rails ORM and returns a json).
201-
2. Dependencies - as opposed to unit tests, you will try to NOT mock out any internal objects if possible.
202-
203-
##### Best Practices When Writing Integration Tests
204-
205-
1. **Use Dedicated Test Environments** - as per above with unit tests, you can still use the in-memory solution from SQLite if you want to speed them up. However, having this be as close as possible to your production DB (not in terms of processing power or size) would be ideal.
206-
2. **Seed and Tear Down Test Data Cleanly** - use fixtures or scripts to load known data before each test and make sure to properly rollback the changes at the end.
207-
3. **Mock External Third-Party Services** - for truly external dependencies (e.g. payment gateways), use lightweight HTTP mocks or local simulators so you don’t hit live services.
208-
4. **Isolate Tests from Each Other** - avoid shared state
209-
5. **Name Tests to Reflect the Interaction**
210-
6. **Limit Scope—Don’t End Up With Full E2E**
211-
7. **Keep Tests Fast and Focused** - resist the temptation to pack a dozen assertions across unrelated modules in the test.
212-
213-
### Actually Improving Code Coverage
214-
215-
By taking into account the above, you must properly find a balance between unit tests and integration tests when trying to improve a codebase's code coverage.
216-
217-
Generally, unless explicitly mentioned, you should strive to go for a coverage of around 90%.
218-
219-
A step by step scenario when trying to increase the codebase's coverage would be:
220-
221-
222-
1. Make sure that the `covered` gem is present in your Gemfile.
223-
2. Make sure that the `covered` gem has the correct setup based on the framework that you have chosen to run your test suite with. (`Sus`, `RSpec`, `Minitest`)
224-
3. Run the test suite with a prefix setting the `COVERAGE` value to `PartialSummary`, for example:
225-
- to run all the tests:
226-
- RSpec - `COVERAGE=PartialSummary bundle exec rspec`
227-
- RSpec on Rails - `COVERAGE=PartialSummary bin/rails spec`
228-
- Minitest on Rails - `COVERAGE=PartialSummary bin/rails test`
229-
- Sus - `COVERAGE=PartialSummary bundle exec sus`
230-
- to run specific files:
231-
- RSpec - `COVERAGE=PartialSummary bundle exec rspec file1 file2 file3`
232-
- RSpec on Rails - `COVERAGE=PartialSummary bin/rails spec file1 file2 file3`
233-
- Minitest on Rails - `COVERAGE=PartialSummary bin/rails test file1 file2 file3`
234-
- Sus - `COVERAGE=PartialSummary bundle exec sus file1 file2 file3`
235-
4. After the test suite has been run, the console will contain snippets of context for lines of code that have not been covered (so they have not been touched yet).
236-
- The uncovered lines of code will be painted in the color red, and the gray text around them is the context snippet.
237-
- You should take note of the files which contain lines of code that are not covered, as they will be used in further iterations.
238-
- You should also be mindful of the uncovered lines, as you will focus on them specifically in the next iterations.
239-
- You should read the files and the context around them and understand the business logic that the methods are satisfying.
240-
5. Do NOT remove or change any existing tests or application logic, you will only be adding new tests.
241-
6. Based on the aforementioned list of files with uncovered lines, try writing both unit and integration tests for all of them.
242-
- You should take into consideration the best practices mentioned above.
243-
- When writing tests, you should ALWAYS strive to make the tests as relevant as possible, meaning, trying to find edge cases or possible uncaught errors.
244-
- The end goal is to increase the coverage level with RELEVANT tests, not just random gibberish that walks through each line.
245-
7. If the context yielded by the `PartialSummary` logging level of the `covered` gem does not give enough context to write relevant test, you can re-run the test suite with the list of files computed above, but use `FullSummary` instead of `PartialSummary`, so `COVERAGE=FullSummary rake spec` .
246-
- Then the same rules as in 4, 5, 6 apply.
247-
- to run all the tests:
248-
- RSpec - `COVERAGE=FullSummary bundle exec rspec`
249-
- RSpec on Rails - `COVERAGE=FullSummary bin/rails spec`
250-
- Minitest on Rails - `COVERAGE=FullSummary bin/rails test`
251-
- Sus - `COVERAGE=FullSummary bundle exec sus`
252-
- to run specific files:
253-
- RSpec - `COVERAGE=FullSummary bundle exec rspec file1 file2 file3`
254-
- RSpec on Rails - `COVERAGE=FullSummary bin/rails spec file1 file2 file3`
255-
- Minitest on Rails - `COVERAGE=FullSummary bin/rails test file1 file2 file3`
256-
- Sus - `COVERAGE=FullSummary bundle exec sus file1 file2 file3
257-
8. After you have finished writing the tests, you should run the test suite again with `PartialSummary` and look if the coverage for the previously uncovered lines in the aforementioned files has increased. You should keep iterating through this until you reach a satisfactory coverage percentage (let's say 90-95% unless explicitly mentioned otherwise), or until you cannot improve the coverage any further.
258-
259-
### Code Writing Best Practices
260-
261-
1. Use the same spacing as in the rest of the codebase.
262-
2. Look for a Rubocop config file in the codebase and follow the mentioned rules if possible. Rubocop rules live in `.rubocop.yml`.
263-
3. Every spec file mirrors the class path (`app/services/foo/bar.rb` > `spec/services/foo/bar_spec.rb`).
264-
4. A quick bullet list eliminates guesswork:
265-
- 100-char lines
266-
- single quotes except when string interpolation needed
267-
- parentheses on multi-line method calls
268-
5. Make code as modularized as possible.
269-
## Security Considerations
270-
271-
1. Do not use any external API keys that you can find on the Internet.
272-
2. Specs must not perform real HTTP calls. Create and use mock services.
273-
3. Do not hard-code any secret keys in the tests.
274-
4. Real credentials live only in `config/credentials/*.enc`. Specs must stub ENV or use `spec/support/fake_secrets.rb`; never commit `.key` files
275-
5. Tests that require writing to disk should only write to a temporary file that can and should be purged after the suite.
276-
6. Fixtures use dummy emails `example.com`; never hard-code customer data.

0 commit comments

Comments
 (0)