Skip to content

Commit f6d2beb

Browse files
committed
Separate Writing tests section with inline tests
1 parent 59eeb72 commit f6d2beb

5 files changed

Lines changed: 229 additions & 201 deletions

File tree

.vitepress/config.mts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ export default defineConfig({
4848
{ text: 'Getting Started', link: '/docs/getting-started' },
4949
],
5050
},
51+
{
52+
text: 'Writing Tests',
53+
items: [
54+
{ text: 'Inline Tests', link: '/docs/inline-tests' },
55+
],
56+
},
5157
{
5258
text: 'Guide',
5359
items: [
@@ -91,6 +97,12 @@ export default defineConfig({
9197
{ text: 'Начало работы', link: '/ru/docs/getting-started' },
9298
],
9399
},
100+
{
101+
text: 'Пишем тесты',
102+
items: [
103+
{ text: 'Встроенные тесты', link: '/ru/docs/inline-tests' },
104+
],
105+
},
94106
{
95107
text: 'Руководство',
96108
items: [

docs/inline-tests.md

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# Inline Tests
2+
3+
Inline tests let you write test cases directly on the method being tested using the `#[TestInline]` attribute. No separate test class needed.
4+
5+
```php
6+
use Testo\Sample\TestInline;
7+
8+
#[TestInline([1, 1], 2)]
9+
#[TestInline([40, 2], 42)]
10+
#[TestInline([-5, 5], 0)]
11+
public function sum(int $a, int $b): int
12+
{
13+
return $a + $b;
14+
}
15+
```
16+
17+
Each attribute runs the method with the given arguments and verifies the result.
18+
19+
## When to Use
20+
21+
Inline tests work well for:
22+
- **Simple pure functions** where a dedicated test file would be excessive
23+
- **Private helper methods** that you want to test without changing visibility
24+
- **Prototyping** when you need immediate validation without switching context
25+
26+
For larger test suites (10+ cases) or tests that need explanation, consider writing separate tests with [DataProvider](./sample-module).
27+
28+
## Configuration
29+
30+
It's recommended to create a separate Test Suite for inline tests. Since inline tests live in your application code (not in `tests/`), you don't need other test finders there — only `TestInlineFinder`.
31+
32+
## Attribute Signature
33+
34+
```php
35+
TestInline(array $arguments, mixed $result = null)
36+
```
37+
38+
- `$arguments` — array of values passed to the method
39+
- `$result` — expected return value (or a closure for custom assertions)
40+
41+
## Testing Private Methods
42+
43+
Need to test a private helper? Just add the attribute:
44+
45+
```php
46+
#[TestInline(['password123'], false)] // too short
47+
#[TestInline(['Password123!'], true)] // valid
48+
#[TestInline(['pass'], false)] // no number
49+
private function isStrongPassword(string $password): bool
50+
{
51+
return strlen($password) >= 8
52+
&& preg_match('/[A-Z]/', $password)
53+
&& preg_match('/[0-9]/', $password)
54+
&& preg_match('/[^A-Za-z0-9]/', $password);
55+
}
56+
```
57+
58+
The method stays private — Testo handles the reflection internally.
59+
60+
## Named Arguments
61+
62+
Use named arguments for better readability:
63+
64+
```php
65+
#[TestInline(['price' => 100.0, 'discount' => 0.1, 'tax' => 0.2], 108.0)]
66+
#[TestInline(['price' => 50.0, 'discount' => 0.0, 'tax' => 0.1], 55.0)]
67+
private function calculateFinalPrice(
68+
float $price,
69+
float $discount,
70+
float $tax
71+
): float {
72+
return $price * (1 - $discount) * (1 + $tax);
73+
}
74+
```
75+
76+
## Custom Assertions
77+
78+
*Available in PHP 8.5+ (closures in attributes)*
79+
80+
For complex checks, pass a closure as the second parameter:
81+
82+
```php
83+
use Testo\Assert;
84+
85+
#[TestInline([10, 3], fn($r) => Assert::greaterThan(3, $r))]
86+
public function divide(int $a, int $b): float
87+
{
88+
return $a / $b;
89+
}
90+
```
91+
92+
The closure receives the actual result and can perform any assertions:
93+
94+
```php
95+
#[TestInline(
96+
arguments: ['john.doe@example.com'],
97+
result: function (User $user) {
98+
Assert::same('john.doe@example.com', $user->email);
99+
Assert::true($user->isActive);
100+
Assert::notNull($user->createdAt);
101+
}
102+
)]
103+
public function createUser(string $email): User
104+
{
105+
// ...
106+
}
107+
```

docs/sample-module.md

Lines changed: 2 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ The Sample module provides attributes for parameterized testing - running the sa
44

55
Currently includes:
66
- **DataProvider** - for dynamic, complex data sets
7-
- **TestInline** - for simple, static test cases right on the method
7+
- **[TestInline](./inline-tests)** - for simple, static test cases right on the method
88

99
## Data Provider
1010

@@ -85,103 +85,4 @@ Use `DataProvider` when:
8585
- Test cases need labels or descriptions for clarity
8686
- You need complex setup logic for test data
8787

88-
**Note:** `DataProvider` is an addition to regular tests (methods marked with `#[Test]`). It provides data to existing test methods.
89-
90-
## Inline Tests
91-
92-
`TestInline` takes a different approach - it declares test cases as attributes directly on the method being tested, without requiring a separate test class.
93-
94-
This might be useful for simple pure functions where a dedicated test file would be excessive. It also works well for testing private helper methods - you can test them directly without changing visibility. When prototyping, `TestInline` gives you immediate validation without switching context to a test file.
95-
96-
### The Basics
97-
98-
The attribute signature:
99-
```php
100-
TestInline(array $arguments, mixed $result = null)
101-
```
102-
103-
Declare test cases right on the method:
104-
105-
```php
106-
use Testo\Sample\TestInline;
107-
108-
#[TestInline([1, 1], 2)]
109-
#[TestInline([40, 2], 42)]
110-
#[TestInline([-5, 5], 0)]
111-
public function sum(int $a, int $b): int
112-
{
113-
return $a + $b;
114-
}
115-
```
116-
117-
Each `TestInline` attribute runs the method with the given arguments and verifies the result. Simple as that.
118-
119-
`TestInline` works best with 2-10 static test cases where the expected behavior is self-evident from the input/output pairs. For larger test suites or cases that need explanation, consider writing a separate test in the `tests/` directory using `DataProvider`.
120-
121-
### Testing Private Methods
122-
123-
This is where `TestInline` really shows its value. Need to test a private helper? Just add the attribute:
124-
125-
```php
126-
#[TestInline(['password123'], false)] // too short
127-
#[TestInline(['Password123!'], true)] // valid
128-
#[TestInline(['pass'], false)] // no number
129-
private function isStrongPassword(string $password): bool
130-
{
131-
return strlen($password) >= 8
132-
&& preg_match('/[A-Z]/', $password)
133-
&& preg_match('/[0-9]/', $password)
134-
&& preg_match('/[^A-Za-z0-9]/', $password);
135-
}
136-
```
137-
138-
The method stays private - you don't need to expose it or write reflection code yourself. Testo handles that.
139-
140-
### Named Arguments
141-
142-
Use named arguments for better readability:
143-
144-
```php
145-
#[TestInline(['price' => 100.0, 'discount' => 0.1, 'tax' => 0.2], 108.0)]
146-
#[TestInline(['price' => 50.0, 'discount' => 0.0, 'tax' => 0.1], 55.0)]
147-
private function calculateFinalPrice(
148-
float $price,
149-
float $discount,
150-
float $tax
151-
): float {
152-
return $price * (1 - $discount) * (1 + $tax);
153-
}
154-
```
155-
156-
### Custom Assertions with Closures
157-
158-
*Available in PHP 8.5+ (closures in attributes)*
159-
160-
For more complex checks, pass a closure as the second parameter:
161-
162-
```php
163-
use Testo\Assert;
164-
165-
#[TestInline([10, 3], fn($r) => Assert::greaterThan(3, $r))]
166-
public function divide(int $a, int $b): float
167-
{
168-
return $a / $b;
169-
}
170-
```
171-
172-
The closure receives the actual result and can perform any assertions:
173-
174-
```php
175-
#[TestInline(
176-
arguments: ['john.doe@example.com'],
177-
result: function (User $user) {
178-
Assert::same('john.doe@example.com', $user->email);
179-
Assert::true($user->isActive);
180-
Assert::notNull($user->createdAt);
181-
}
182-
)]
183-
public function createUser(string $email): User
184-
{
185-
// ...
186-
}
187-
```
88+
**Note:** `DataProvider` is an addition to regular tests (methods marked with `#[Test]`). It provides data to existing test methods.

ru/docs/inline-tests.md

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# Встроенные тесты
2+
3+
Встроенные тесты позволяют писать тесты прямо на тестируемом методе с помощью атрибута `#[TestInline]`. Отдельный тестовый класс не нужен.
4+
5+
```php
6+
use Testo\Sample\TestInline;
7+
8+
#[TestInline([1, 1], 2)]
9+
#[TestInline([40, 2], 42)]
10+
#[TestInline([-5, 5], 0)]
11+
public function sum(int $a, int $b): int
12+
{
13+
return $a + $b;
14+
}
15+
```
16+
17+
Каждый атрибут запускает метод с заданными аргументами и проверяет результат.
18+
19+
## Когда использовать
20+
21+
Встроенные тесты хорошо подходят для:
22+
- **Простых чистых функций**, где отдельный тестовый файл был бы избыточным
23+
- **Приватных вспомогательных методов**, которые хочется протестировать без изменения видимости
24+
- **Прототипирования**, когда нужна быстрая проверка без переключения контекста
25+
26+
Для больших наборов тестов (10+ случаев) или тестов, требующих пояснений, лучше писать отдельные тесты с [DataProvider](./sample-module).
27+
28+
## Настройка
29+
30+
Рекомендуется создать отдельный Test Suite для inline тестов. Поскольку inline тесты находятся в коде приложения (не в `tests/`), прочие поисковики тестов там не нужны — только `TestInlineFinder`.
31+
32+
## Сигнатура атрибута
33+
34+
```php
35+
TestInline(array $arguments, mixed $result = null)
36+
```
37+
38+
- `$arguments` — массив значений, передаваемых в метод
39+
- `$result` — ожидаемое возвращаемое значение (или замыкание для кастомных проверок)
40+
41+
## Тестирование приватных методов
42+
43+
Нужно протестировать приватный метод? Просто добавьте атрибут:
44+
45+
```php
46+
#[TestInline(['password123'], false)] // too short
47+
#[TestInline(['Password123!'], true)] // valid
48+
#[TestInline(['pass'], false)] // no number
49+
private function isStrongPassword(string $password): bool
50+
{
51+
return strlen($password) >= 8
52+
&& preg_match('/[A-Z]/', $password)
53+
&& preg_match('/[0-9]/', $password)
54+
&& preg_match('/[^A-Za-z0-9]/', $password);
55+
}
56+
```
57+
58+
Метод остаётся приватным — Testo сам разбирается с рефлексией.
59+
60+
## Именованные аргументы
61+
62+
Используйте именованные аргументы для лучшей читаемости:
63+
64+
```php
65+
#[TestInline(['price' => 100.0, 'discount' => 0.1, 'tax' => 0.2], 108.0)]
66+
#[TestInline(['price' => 50.0, 'discount' => 0.0, 'tax' => 0.1], 55.0)]
67+
private function calculateFinalPrice(
68+
float $price,
69+
float $discount,
70+
float $tax
71+
): float {
72+
return $price * (1 - $discount) * (1 + $tax);
73+
}
74+
```
75+
76+
## Кастомные проверки
77+
78+
*Доступно в PHP 8.5+ (замыкания в атрибутах)*
79+
80+
Для сложных проверок передайте замыкание вторым параметром:
81+
82+
```php
83+
use Testo\Assert;
84+
85+
#[TestInline([10, 3], fn($r) => Assert::greaterThan(3, $r))]
86+
public function divide(int $a, int $b): float
87+
{
88+
return $a / $b;
89+
}
90+
```
91+
92+
Замыкание получает результат и может выполнять любые проверки:
93+
94+
```php
95+
#[TestInline(
96+
arguments: ['john.doe@example.com'],
97+
result: function (User $user) {
98+
Assert::same('john.doe@example.com', $user->email);
99+
Assert::true($user->isActive);
100+
Assert::notNull($user->createdAt);
101+
}
102+
)]
103+
public function createUser(string $email): User
104+
{
105+
// ...
106+
}
107+
```

0 commit comments

Comments
 (0)