Skip to content

Commit fb2db2d

Browse files
committed
Add comprehensive testing guidelines for contributor alignment
To ensure our quality standards remain consistent as the project grows, I’ve compiled these implicit practices into a formal set of testing guidelines. This documentation is a shared philosophy designed to keep our tests maintainable and meaningful. I’ve focused on three core pillars: 1. Data-Driven Testing: Encouraging the use of data providers to exhaustively cover edge cases without cluttering test logic. 2. Black-Box Philosophy: Prioritizing the testing of public behavior over internal implementation details to ensure refactors don't unnecessarily break tests. 3. Real Instances over Mocks: Emphasizing the use of actual objects wherever possible. This avoids the "brittle test" trap where mocks pass even when the underlying integration is broken. Assisted-by: OpenCode (GLM-4.6) Assisted-by: Gemini 3 (Thinking)
1 parent f5f216a commit fb2db2d

3 files changed

Lines changed: 173 additions & 0 deletions

File tree

AGENTS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ This file contains instructions for AI agents working on this repository.
1515
## Testing & Quality Standards
1616

1717
- **Unit tests**: Located in `tests/Unit/`
18+
- **Testing guidelines**: See `docs/contributing/testing-guidelines.md` for patterns
19+
- **Data-driven testing**: Use comprehensive providers with descriptive keys
20+
- **Arrange-Act-Assert**: Structure tests with clear setup, action, and assertion sections
21+
- **No mocks**: Create custom test implementations instead of PHPUnit mocks
1822
- **All contributions must include tests that pass**
1923
- **Code style**: Follow PSR-12 coding standard
2024
- **Static analysis**: Use PHPStan, fix any issues before submitting

CONTRIBUTING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ For detailed guidelines, see the [contributing documentation](docs/contributing/
77
- [Getting Started](docs/contributing/getting-started.md) - Set up your development environment
88
- [Guidelines](docs/contributing/guidelines.md) - Best practices
99
- [Adding Formatters](docs/contributing/adding-formatters.md) - How to create new formatters
10+
- [Testing Guidelines](docs/contributing/testing-guidelines.md) - Testing standards and patterns
1011
- [Development](docs/contributing/development.md) - Testing, linting, and static analysis
1112
- [Submitting Changes](docs/contributing/submitting-changes.md) - Pull request process
1213
- [Commit Guidelines](docs/contributing/commit-guidelines.md) - How to write good commit messages
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
# Testing Guidelines
2+
3+
This document outlines the testing standards and practices for contributions to StringFormatter.
4+
5+
## Core Testing Principles
6+
7+
1. Use Data-Driven Testing: Create comprehensive data providers with descriptive string keys
8+
2. Test behaviors, not implementations: Focus on input/output results through public interfaces
9+
3. Use real instances: Avoid PHPUnit mocks - create custom test implementations when needed
10+
4. Black box testing: Verify interactions through results, not internal state
11+
12+
## Test Structure Standards
13+
14+
### Class Organization
15+
16+
- All test classes must be `final`
17+
- Use the `Respect\StringFormatter\Test\Unit` namespace
18+
- Add `#[CoversClass(ClassName::class)]` attribute for code coverage
19+
- Extend `PHPUnit\Framework\TestCase`
20+
21+
### Method Organization
22+
23+
- Use `#[Test]` attribute for test methods
24+
- Use `#[DataProvider('methodName')]` for parameterized tests
25+
- Follow naming pattern: `itShould[Behavior]When[Condition]` or `itShould[ExpectedBehavior]`
26+
27+
## Data Provider Patterns
28+
29+
```php
30+
/ @return array<string, array{0: InputType, 1: TemplateType, 2: ExpectedType}> */
31+
public static function providerForFeatureName(): array
32+
{
33+
return [
34+
'descriptive test case name' => [
35+
$input,
36+
$template,
37+
$expected,
38+
],
39+
'another test case' => [
40+
$input2,
41+
$template2,
42+
$expected2,
43+
],
44+
];
45+
}
46+
```
47+
48+
### Provider Requirements
49+
50+
- Use descriptive string keys for each test case
51+
- Document array structure with PHPDoc array shapes
52+
- Include edge cases, error conditions, and real-world scenarios
53+
- Test Unicode support and internationalization when applicable
54+
55+
## Test Structure: Arrange-Act-Assert
56+
57+
Follow the Arrange-Act-Assert pattern for clear test organization:
58+
59+
```php
60+
#[Test]
61+
public function itShouldFormatTemplateCorrectly(): void
62+
{
63+
// Arrange: Set up test data and objects
64+
$parameters = ['name' => 'John'];
65+
$template = 'Hello {{name}}!';
66+
$expected = 'Hello John!';
67+
68+
// Act: Execute the method being tested
69+
$formatter = new FormatterClass($parameters);
70+
$actual = $formatter->format($template);
71+
72+
// Assert: Verify the result
73+
self::assertSame($expected, $actual);
74+
}
75+
```
76+
77+
### Pattern Benefits
78+
79+
- **Arrange**: Clearly separates test setup
80+
- **Act**: Highlights the specific behavior being tested
81+
- **Assert**: Makes verification explicit and easy to read
82+
83+
## Assertion Patterns
84+
85+
### Primary Assertions
86+
87+
```php
88+
// Following Arrange-Act-Assert pattern:
89+
// 1. Arrange: Create formatter and setup (done above)
90+
// 2. Act: Call format method (done above)
91+
// 3. Assert: Verify result
92+
self::assertSame($expected, $actual);
93+
```
94+
95+
### Exception Testing
96+
97+
```php
98+
#[Test]
99+
public function itShouldThrowExceptionForInvalidInput(): void
100+
{
101+
$this->expectException(InvalidFormatterException::class);
102+
$this->expectExceptionMessage('Specific error message');
103+
104+
new FormatterClass($invalidInput);
105+
}
106+
```
107+
108+
## Coverage Requirements
109+
110+
### Test Coverage Areas
111+
112+
- Happy Path: Primary functionality with valid inputs
113+
- Edge Cases: Empty inputs, boundary conditions, malformed data
114+
- Error Conditions: Invalid inputs, exception scenarios
115+
- Type Support: All PHP types when the class under test handler mixed types directly
116+
- Unicode: International characters, emoji, mixed languages
117+
- Real-world Usage: Email templates, log messages, URLs, etc.
118+
119+
## Code Quality Standards
120+
121+
### Test Organization
122+
123+
- Each test method validates one specific behavior
124+
- Group related tests logically by feature
125+
- Keep test setup minimal and inline
126+
- Use descriptive test case names in data providers
127+
128+
### Documentation Standards
129+
130+
- Document test methods with PHPDoc when behavior isn't obvious
131+
- Use type annotations for complex parameters in data providers
132+
- Include real-world examples in test cases
133+
134+
## Dependencies and Mocks
135+
136+
### Preferred Approach
137+
138+
- Create real instances of any objects needed for testing
139+
- Use custom test implementations instead of PHPUnit mocks
140+
- Test through public APIs like `format()` and `formatWith()`
141+
142+
### When Custom Implementations Are Needed
143+
144+
```php
145+
class DumpStringifier implements Stringifier
146+
{
147+
public function stringify(mixed $value): string
148+
{
149+
return var_export($value, true);
150+
}
151+
}
152+
```
153+
154+
## Static Analysis Compatibility
155+
156+
- Write tests that pass PHPStan static analysis
157+
- Use proper type annotations in PHPDoc
158+
- Ensure test code follows PSR-12 coding standard
159+
160+
## Examples from Codebase
161+
162+
See these test files for reference patterns:
163+
164+
- `tests/Unit/PlaceholderFormatterTest.php` - Data-driven testing with comprehensive providers
165+
- `tests/Unit/PatternFormatterTest.php` - Exception testing and edge case coverage
166+
- `tests/Unit/JavascriptFormatterTest.php` - Unicode and internationalization testing
167+
168+
Remember: Tests should be clear, maintainable, and focused on verifying desired behaviors rather than implementation details.

0 commit comments

Comments
 (0)