Skip to content

Commit a17c4a1

Browse files
committed
Publish "Beta Testing is Open" blog post
1 parent fa7afea commit a17c4a1

8 files changed

Lines changed: 583 additions & 19 deletions

File tree

.vitepress/theme/style.css

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,12 +707,14 @@ html {
707707
border-radius: 4px;
708708
cursor: help;
709709
transition: background 0.2s;
710+
white-space: nowrap;
710711
}
711712

712713
.class-ref {
713714
color: var(--vp-code-color);
714715
background: var(--vp-code-bg);
715716
padding: 3px 6px;
717+
white-space: nowrap;
716718
}
717719

718720
.class-ref:hover {
@@ -799,3 +801,81 @@ a.func-ref:hover .func-ref-tooltip-short {
799801
.func-ref-unlinked {
800802
color: var(--vp-c-text-1);
801803
}
804+
805+
/* Featured blog post card on homepage */
806+
.featured-post {
807+
max-width: 700px;
808+
margin: 48px auto 0;
809+
}
810+
811+
.featured-post-card {
812+
display: flex;
813+
align-items: center;
814+
gap: 1.5rem;
815+
padding: 1.25rem;
816+
border-radius: 12px;
817+
background: var(--vp-c-bg-soft);
818+
border: 1px solid var(--vp-c-divider);
819+
transition: border-color 0.25s;
820+
}
821+
822+
.featured-post-card:hover {
823+
border-color: var(--vp-c-brand-1);
824+
}
825+
826+
.featured-post-image {
827+
flex-shrink: 0;
828+
width: 200px;
829+
aspect-ratio: 16 / 10;
830+
overflow: hidden;
831+
border-radius: 8px;
832+
}
833+
834+
.featured-post-image img {
835+
width: 100%;
836+
height: 100%;
837+
object-fit: cover;
838+
transition: transform 0.2s ease;
839+
}
840+
841+
.featured-post-card:hover .featured-post-image img {
842+
transform: scale(1.05);
843+
}
844+
845+
.featured-post-body {
846+
display: flex;
847+
flex-direction: column;
848+
gap: 0.4rem;
849+
min-width: 0;
850+
}
851+
852+
.featured-post-title {
853+
font-size: 1.25rem;
854+
font-weight: 600;
855+
line-height: 1.3;
856+
color: var(--vp-c-text-1);
857+
}
858+
859+
.featured-post-description {
860+
margin: 0;
861+
color: var(--vp-c-text-2);
862+
font-size: 0.95rem;
863+
line-height: 1.5;
864+
}
865+
866+
a.featured-post-card,
867+
a.featured-post-card:hover {
868+
text-decoration: none;
869+
color: inherit;
870+
}
871+
872+
@media (max-width: 640px) {
873+
.featured-post-card {
874+
flex-direction: column;
875+
}
876+
877+
.featured-post-image {
878+
width: 100%;
879+
aspect-ratio: 16 / 9;
880+
}
881+
}

blog/beta-testo.md

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
---
2+
title: "Beta Testing is Open!"
3+
outline: [2, 3]
4+
date: 2026-04-01
5+
description: "Start testing with Testo today and help make it better before the release!"
6+
image: /blog/beta-testo/img-0.jpg
7+
author: Aleksei Gagarin
8+
faqLevel: false
9+
---
10+
11+
# Beta Testing is Open!
12+
13+
## A little marketing
14+
15+
1. Testo plays nicely with any libraries and tools, causing no conflicts:
16+
- No PHPUnit dependency. It's not another wrapper around it, it's a full-fledged framework built from scratch.
17+
- Doesn't patch `nikic/php-parser` and doesn't even [use it](https://github.com/sebastianbergmann/phpunit/issues/6381).
18+
- PHP 8.2+ for the widest version support.
19+
20+
2. AI agents can easily generate Testo tests. Just feed them `llms.txt` ([docs](/docs/ai-agents.md)).
21+
22+
3. Thanks to the plugin system, you can shape Testo into exactly what you need. No limitations beyond immutability.
23+
- Every Testo feature is a plugin that can be enabled or disabled at will.
24+
- Writing your own plugin? A couple dozen lines of code and it's up and running.
25+
- Each Test Suite can have its own set of plugins.
26+
27+
4. Go beyond conventional testing:
28+
- Need to test right inside `src`? There are already [inline tests](/docs/plugins/inline.md) and [benchmarks](/docs/plugins/bench.md) for that.
29+
- Want to create a custom attribute with cool logic? Easy. <attr>\Testo\Retry</attr> is a great example.
30+
- The pipeline and middleware system, event system, and plugins give you full control over how the framework behaves.
31+
32+
5. Made by a developer for developers.
33+
- No legacy like abstract `TestCase`.
34+
- Minimal boilerplate thanks to attributes.
35+
- Type safety even in assertions.
36+
- Familiar OOP and PHP syntax, no magic or DSL.
37+
38+
6. A fully featured [PHPStorm plugin](https://plugins.jetbrains.com/plugin/28842-testo) is also available.
39+
40+
41+
**Ready to give it a try?**
42+
43+
## Installation and Setup
44+
45+
Just 3 steps:
46+
47+
1. Install Testo via Composer:
48+
```bash
49+
composer require --dev testo/testo
50+
```
51+
52+
2. Create `testo.php` in the project root:
53+
54+
```php
55+
<?php
56+
57+
declare(strict_types=1);
58+
59+
use Testo\Application\Config\ApplicationConfig;
60+
use Testo\Application\Config\SuiteConfig;
61+
62+
return new ApplicationConfig(
63+
suites: [
64+
new SuiteConfig(
65+
name: 'Sources',
66+
location: ['src'],
67+
),
68+
new SuiteConfig(
69+
name: 'Tests',
70+
location: ['tests'],
71+
),
72+
],
73+
);
74+
```
75+
76+
::: question What is this file?
77+
Testo is configured with a PHP file that returns an <class>\Testo\Application\Config\ApplicationConfig</class> object.
78+
If the file doesn't exist, Testo will try to run tests from the `tests` folder with default settings.
79+
80+
Here we defined two test suites:
81+
- `Sources` for inline tests and benchmarks right in the project code, in the `src` folder;
82+
- `Tests` for regular unit tests in the `tests` folder.
83+
:::
84+
85+
3. Install the PHPStorm plugin:
86+
87+
<JetBrainsPlugin />
88+
89+
Run tests directly from PHPStorm using the plugin, or via CLI:
90+
91+
```bash
92+
./vendor/bin/testo
93+
```
94+
95+
## First Tests
96+
97+
### Unit Test
98+
99+
A test is a regular class with methods marked by the <attr>\Testo\Test</attr> attribute. No base class inheritance:
100+
101+
```php
102+
final class OrderTest
103+
{
104+
#[Test]
105+
public function calculatesTotal(): void
106+
{
107+
$order = new Order();
108+
$order->addItem('Book', price: 15.0, quantity: 2);
109+
$order->addItem('Pen', price: 3.0, quantity: 5);
110+
111+
Assert::same($order->total(), 45.0);
112+
}
113+
114+
#[Test]
115+
#[DataSet([100.0, 10, 90.0], '10% off')]
116+
#[DataSet([100.0, 0, 100.0], 'no discount')]
117+
#[DataSet([0.0, 50, 0.0], 'zero price')]
118+
public function appliesDiscount(float $price, int $percent, float $expected): void
119+
{
120+
$result = Order::applyDiscount($price, $percent);
121+
122+
Assert::same($result, $expected);
123+
}
124+
125+
#[Test]
126+
#[ExpectException(InsufficientFundsException::class)]
127+
public function cannotOverdraw(): never
128+
{
129+
new Account(balance: 100)->withdraw(200);
130+
}
131+
}
132+
```
133+
134+
The <class>\Testo\Assert</class> facade uses an intuitive argument order: `$actual` (the value being checked) first, then `$expected` (the expected value). This differs from the legacy xUnit approach.
135+
136+
And here's what typed assertion chains look like:
137+
138+
```php
139+
Assert::string($email)->contains('@');
140+
141+
Assert::int($age)->greaterThan(0)->lessThan(150);
142+
143+
Assert::array($items)
144+
->hasKeys('id', 'name')
145+
->isList()
146+
->notEmpty();
147+
148+
Assert::json($response->body())
149+
->isObject()
150+
->hasKeys('data', 'meta');
151+
```
152+
153+
### Inline Tests
154+
155+
Test your methods right where they're declared. No separate test file needed. The <attr>\Testo\Inline\TestInline</attr> attribute runs the method with given arguments and checks the result. Works even with private methods:
156+
157+
```php
158+
// src/Money.php
159+
final class Money
160+
{
161+
#[TestInline(['price' => 100.0, 'discount' => 0.1, 'tax' => 0.2], 108.0)]
162+
#[TestInline(['price' => 50.0, 'discount' => 0.0, 'tax' => 0.1], 55.0)]
163+
private static function calculateFinalPrice(
164+
float $price,
165+
float $discount,
166+
float $tax,
167+
): float {
168+
return $price * (1 - $discount) * (1 + $tax);
169+
}
170+
}
171+
```
172+
173+
Perfect for pure functions and rapid prototyping. The test lives next to the code and gets updated along with it.
174+
175+
### Benchmarks
176+
177+
Instantly compare function performance without any boilerplate: just add the <attr>\Testo\Bench</attr> attribute to a function and you're good to go:
178+
179+
```php
180+
#[Bench(
181+
callables: [
182+
'multiply' => 'viaMultiply',
183+
'shift' => 'viaShift',
184+
],
185+
arguments: [1, 5_000],
186+
calls: 2_000_000,
187+
)]
188+
function viaDivision(int $a, int $b): int
189+
{
190+
$d = $b - $a + 1;
191+
return (int) (($d - 1) * $d / 2) + $a * $d;
192+
}
193+
194+
function viaMultiply(int $a, int $b): int
195+
{
196+
$d = $b - $a + 1;
197+
return (int) (($d - 1) * $d * 0.5) + $a * $d;
198+
}
199+
200+
function viaShift(int $a, int $b): int
201+
{
202+
$d = $b - $a + 1;
203+
return ((($d - 1) * $d) >> 1) + $a * $d;
204+
}
205+
```
206+
207+
```
208+
+---+----------+-------+---------+------------------+--------+
209+
| # | Name | Iters | Calls | Avg Time | RStDev |
210+
+---+----------+-------+---------+------------------+--------+
211+
| 2 | current | 10 | 2000000 | 75.890µs | ±0.79% |
212+
| 3 | multiply | 10 | 2000000 | 78.821µs (+3.9%) | ±0.47% |
213+
| 1 | shift | 10 | 2000000 | 70.559µs (-7.0%) | ±0.70% |
214+
+---+----------+-------+---------+------------------+--------+
215+
```
216+
217+
## Interested?
218+
219+
If Testo caught your attention and you'd like to learn more, check out these articles:
220+
221+
- ["To the Collider!"](./collider.md) — about benchmarks and performance comparison.
222+
- ["Testo. Assert and Expect"](./assert-and-expect.md) — about the new and legacy API for assertions and expectations.
223+
- ["Data Providers"](./data-providers.md) — about powerful and flexible data providers for tests.
224+
225+
Give a star on [GitHub](https://github.com/php-testo/testo) and rate the [PHPStorm plugin](https://plugins.jetbrains.com/plugin/28842-testo). It really helps Testo gain visibility.
226+
227+
## What's Next?
228+
229+
Beta testing is underway and we're moving toward the release.
230+
The public API has stabilized, but there are still a few things to finish:
231+
232+
- Refine CLI and PHPStorm report output, add diff.
233+
- Small things like STDOUT capture and PHP error handling.
234+
- Parallel test execution and isolated execution in a separate process.
235+
- Fine-tune minor things in benchmarks and internals.
236+
- Organizational matters like splitting the monorepo and finishing the documentation.
237+
238+
Code coverage and mocks might also make it to the release, but no promises.
239+
240+
You can help by testing and providing feedback to make the release as smooth as possible.
241+
Head to [GitHub Issues](https://github.com/php-testo/testo/issues) with ideas, questions, and problems. Let's figure it out together!

index.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,15 +76,16 @@ const declareTabs = [
7676

7777
</script>
7878

79-
<div style="max-width: 700px; margin: 48px auto 0;">
80-
81-
::: warning 🚧 Work in Progress
82-
Testo is still under active development and not ready for production use.
83-
Feel free to explore and experiment, but don't rely on it for real projects yet.
84-
85-
Want to support the project? [Star the repo](https://github.com/php-testo/testo) or [become a sponsor](/sponsor).
86-
:::
87-
79+
<div class="featured-post">
80+
<a href="/blog/beta-testo" class="featured-post-card">
81+
<div class="featured-post-image">
82+
<img src="/blog/beta-testo/img-0.jpg" alt="Beta Testing is Open!" />
83+
</div>
84+
<div class="featured-post-body">
85+
<span class="featured-post-title">Beta Testing is Open!</span>
86+
<p class="featured-post-description">Start testing with Testo today and help make it better before the release!</p>
87+
</div>
88+
</a>
8889
</div>
8990

9091
<div class="home-feature">

public/blog/beta-testo/img-0.jpg

1.22 MB
Loading

0 commit comments

Comments
 (0)