From 4177ca481750917a6a07f3f36ac579947dd44a9b Mon Sep 17 00:00:00 2001 From: Nikolay Gagarinov Date: Thu, 2 Jul 2026 18:58:50 +0500 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20=D0=BF=D0=BE=D0=BA=D0=B0=D0=B7=D1=8B?= =?UTF-8?q?=D0=B2=D0=B0=D1=82=D1=8C=20=D0=B2=D1=8B=D0=B2=D0=BE=D0=B4=20?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=B3=D1=80=D0=B0=D0=BC=D0=BC=D1=8B=20=D1=81?= =?UTF-8?q?=D1=82=D1=83=D0=B4=D0=B5=D0=BD=D1=82=D0=B0=20=D0=BD=D0=B0=20?= =?UTF-8?q?=D0=BF=D0=B0=D0=BD=D0=B5=D0=BB=D0=B8=20=C2=AB=D0=B2=D1=8B=D0=B2?= =?UTF-8?q?=D0=BE=D0=B4=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Тесты выводных уроков глушили вывод: $this->expectOutputString($expected) буферизует stdout студента и молча отбрасывает после assertSame. PHPUnit переиздаёт только «неожиданный» вывод, а expectOutputString делает его «ожидаемым» → на панель он не попадал (студент видел только отчёт). Централизовали перехват в src/TestCase.php: require Solution.php оборачиваем в ob_start/ob_get_clean и печатаем перехваченное через fwrite(STDOUT) в обход буфера PHPUnit — так оно попадает на панель. Добавили хелперы assertOutput() и assertOutputOf() (для уроков, где вывод даёт вызов функции). В выводных тестах expectOutputString → assertOutput; deterministic переведён с getActualOutputForAssertion на assertOutput, два Group-B (define-function, while) — на assertOutputOf. Аналог фиксов Python (#370) и JavaScript (#853). Возвратные уроки и курс Java не менялись. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../10-basics/10-hello-world/SolutionTest.php | 2 +- modules/10-basics/15-tags/SolutionTest.php | 2 +- .../10-basics/20-comments/SolutionTest.php | 2 +- .../30-instructions/SolutionTest.php | 2 +- modules/10-basics/40-testing/SolutionTest.php | 2 +- .../50-syntax-errors/SolutionTest.php | 2 +- .../20-arithmetics/20-basic/SolutionTest.php | 2 +- .../25-operator/SolutionTest.php | 2 +- .../27-commutativity/SolutionTest.php | 2 +- .../30-composition/SolutionTest.php | 2 +- .../40-priority/SolutionTest.php | 2 +- .../20-arithmetics/43-float/SolutionTest.php | 2 +- .../45-linting/SolutionTest.php | 2 +- modules/25-strings/10-quotes/SolutionTest.php | 2 +- .../15-escape-characters/SolutionTest.php | 2 +- .../20-string-concatenation/SolutionTest.php | 2 +- .../25-strings/30-encoding/SolutionTest.php | 2 +- .../10-definition/SolutionTest.php | 2 +- .../30-variables/12-change/SolutionTest.php | 2 +- .../13-variables-naming/SolutionTest.php | 2 +- .../15-variables-expressions/SolutionTest.php | 2 +- .../SolutionTest.php | 2 +- .../19-naming-style/SolutionTest.php | 2 +- .../20-magic-numbers/SolutionTest.php | 2 +- .../100-unicode/SolutionTest.php | 2 +- .../110-locales/SolutionTest.php | 2 +- .../120-startwith/SolutionTest.php | 2 +- .../25-interpolation/SolutionTest.php | 2 +- .../30-symbols/SolutionTest.php | 2 +- .../70-substrings/SolutionTest.php | 2 +- .../90-multiline-strings/SolutionTest.php | 2 +- .../40-primitive-data-types/SolutionTest.php | 2 +- .../SolutionTest.php | 2 +- .../SolutionTest.php | 2 +- .../55-data-types-casting/SolutionTest.php | 2 +- .../100-call/SolutionTest.php | 2 +- .../SolutionTest.php | 2 +- .../SolutionTest.php | 2 +- .../180-variadic-parameters/SolutionTest.php | 2 +- .../270-deterministic/SolutionTest.php | 2 +- .../100-define-function/SolutionTest.php | 3 +-- modules/50-loops/10-while/SolutionTest.php | 3 +-- src/TestCase.php | 27 +++++++++++++++++++ 43 files changed, 69 insertions(+), 44 deletions(-) diff --git a/modules/10-basics/10-hello-world/SolutionTest.php b/modules/10-basics/10-hello-world/SolutionTest.php index a2377bf9..7262da00 100644 --- a/modules/10-basics/10-hello-world/SolutionTest.php +++ b/modules/10-basics/10-hello-world/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = 'Hello, World!'; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/10-basics/15-tags/SolutionTest.php b/modules/10-basics/15-tags/SolutionTest.php index 8d77257f..0eeaee75 100644 --- a/modules/10-basics/15-tags/SolutionTest.php +++ b/modules/10-basics/15-tags/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = 'King in the North!'; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/10-basics/20-comments/SolutionTest.php b/modules/10-basics/20-comments/SolutionTest.php index 94a09e37..6523afbf 100644 --- a/modules/10-basics/20-comments/SolutionTest.php +++ b/modules/10-basics/20-comments/SolutionTest.php @@ -8,7 +8,7 @@ class SolutionTest extends TestCase { public function test() { - $this->expectOutputString(''); + $this->assertOutput(''); $content = file_get_contents('Solution.php'); $this->assertStringContainsString('// TODO: добавить функцию приветствия', $content); } diff --git a/modules/10-basics/30-instructions/SolutionTest.php b/modules/10-basics/30-instructions/SolutionTest.php index aede0059..5851ea72 100644 --- a/modules/10-basics/30-instructions/SolutionTest.php +++ b/modules/10-basics/30-instructions/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = 'RobertStannisRenly'; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/10-basics/40-testing/SolutionTest.php b/modules/10-basics/40-testing/SolutionTest.php index bbf48ac1..567f16c7 100644 --- a/modules/10-basics/40-testing/SolutionTest.php +++ b/modules/10-basics/40-testing/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = '9780262531962'; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/10-basics/50-syntax-errors/SolutionTest.php b/modules/10-basics/50-syntax-errors/SolutionTest.php index 915b3b9f..b1b37d51 100644 --- a/modules/10-basics/50-syntax-errors/SolutionTest.php +++ b/modules/10-basics/50-syntax-errors/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = 'Программа успешно запущена'; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/20-arithmetics/20-basic/SolutionTest.php b/modules/20-arithmetics/20-basic/SolutionTest.php index a246685b..6abf3fc3 100644 --- a/modules/20-arithmetics/20-basic/SolutionTest.php +++ b/modules/20-arithmetics/20-basic/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = '9'; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/20-arithmetics/25-operator/SolutionTest.php b/modules/20-arithmetics/25-operator/SolutionTest.php index a4019612..0229de44 100644 --- a/modules/20-arithmetics/25-operator/SolutionTest.php +++ b/modules/20-arithmetics/25-operator/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = '8729'; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/20-arithmetics/27-commutativity/SolutionTest.php b/modules/20-arithmetics/27-commutativity/SolutionTest.php index d219d54f..2cfc86be 100644 --- a/modules/20-arithmetics/27-commutativity/SolutionTest.php +++ b/modules/20-arithmetics/27-commutativity/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = '2563'; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/20-arithmetics/30-composition/SolutionTest.php b/modules/20-arithmetics/30-composition/SolutionTest.php index e1df4641..4b1141f1 100644 --- a/modules/20-arithmetics/30-composition/SolutionTest.php +++ b/modules/20-arithmetics/30-composition/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = '660'; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/20-arithmetics/40-priority/SolutionTest.php b/modules/20-arithmetics/40-priority/SolutionTest.php index aed274b5..32cc2cfb 100644 --- a/modules/20-arithmetics/40-priority/SolutionTest.php +++ b/modules/20-arithmetics/40-priority/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = '160'; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/20-arithmetics/43-float/SolutionTest.php b/modules/20-arithmetics/43-float/SolutionTest.php index 007d6b3a..0ec88aa0 100644 --- a/modules/20-arithmetics/43-float/SolutionTest.php +++ b/modules/20-arithmetics/43-float/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = '5.5511151231258E-17'; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/20-arithmetics/45-linting/SolutionTest.php b/modules/20-arithmetics/45-linting/SolutionTest.php index b8bfb7bd..6abd7179 100644 --- a/modules/20-arithmetics/45-linting/SolutionTest.php +++ b/modules/20-arithmetics/45-linting/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = '4'; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/25-strings/10-quotes/SolutionTest.php b/modules/25-strings/10-quotes/SolutionTest.php index 5aaa69b7..0e650a76 100644 --- a/modules/25-strings/10-quotes/SolutionTest.php +++ b/modules/25-strings/10-quotes/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = 'The file "user\'s_config.json" was not found.'; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/25-strings/15-escape-characters/SolutionTest.php b/modules/25-strings/15-escape-characters/SolutionTest.php index c7993076..c7347127 100644 --- a/modules/25-strings/15-escape-characters/SolutionTest.php +++ b/modules/25-strings/15-escape-characters/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = "Для разделения строк используйте \"\\n\"\nПример: print_r(\"строка1\\nстрока2\")"; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/25-strings/20-string-concatenation/SolutionTest.php b/modules/25-strings/20-string-concatenation/SolutionTest.php index a4ac3688..37b2c84e 100644 --- a/modules/25-strings/20-string-concatenation/SolutionTest.php +++ b/modules/25-strings/20-string-concatenation/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = 'https://github.com/hexlet/exercises-php'; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/25-strings/30-encoding/SolutionTest.php b/modules/25-strings/30-encoding/SolutionTest.php index a19cfb88..a24ebba2 100644 --- a/modules/25-strings/30-encoding/SolutionTest.php +++ b/modules/25-strings/30-encoding/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = "~\n^\n%\n"; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/30-variables/10-definition/SolutionTest.php b/modules/30-variables/10-definition/SolutionTest.php index 5ae87dbd..26de7446 100644 --- a/modules/30-variables/10-definition/SolutionTest.php +++ b/modules/30-variables/10-definition/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = "https://hexlet.io\nhttps://hexlet.io"; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/30-variables/12-change/SolutionTest.php b/modules/30-variables/12-change/SolutionTest.php index 1cb89bd0..8abcbd24 100644 --- a/modules/30-variables/12-change/SolutionTest.php +++ b/modules/30-variables/12-change/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = 'доставлен'; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/30-variables/13-variables-naming/SolutionTest.php b/modules/30-variables/13-variables-naming/SolutionTest.php index 0621225e..9d32ddb7 100644 --- a/modules/30-variables/13-variables-naming/SolutionTest.php +++ b/modules/30-variables/13-variables-naming/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = "2"; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/30-variables/15-variables-expressions/SolutionTest.php b/modules/30-variables/15-variables-expressions/SolutionTest.php index 61bcaae1..c7422166 100644 --- a/modules/30-variables/15-variables-expressions/SolutionTest.php +++ b/modules/30-variables/15-variables-expressions/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = "125\n863.75"; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/30-variables/18-variable-concatenation/SolutionTest.php b/modules/30-variables/18-variable-concatenation/SolutionTest.php index fc4b6d51..0dcd48d7 100644 --- a/modules/30-variables/18-variable-concatenation/SolutionTest.php +++ b/modules/30-variables/18-variable-concatenation/SolutionTest.php @@ -14,6 +14,6 @@ public function test() Ожидаемая дата доставки — 3 рабочих дня. HERE; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/30-variables/19-naming-style/SolutionTest.php b/modules/30-variables/19-naming-style/SolutionTest.php index f2def806..fed482e2 100644 --- a/modules/30-variables/19-naming-style/SolutionTest.php +++ b/modules/30-variables/19-naming-style/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = "2000"; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/30-variables/20-magic-numbers/SolutionTest.php b/modules/30-variables/20-magic-numbers/SolutionTest.php index 827829b1..99395244 100644 --- a/modules/30-variables/20-magic-numbers/SolutionTest.php +++ b/modules/30-variables/20-magic-numbers/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = "Ящиков на складе:\n102"; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/31-advanced-strings/100-unicode/SolutionTest.php b/modules/31-advanced-strings/100-unicode/SolutionTest.php index f0ba2ef6..1424b627 100644 --- a/modules/31-advanced-strings/100-unicode/SolutionTest.php +++ b/modules/31-advanced-strings/100-unicode/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = "13\n7\n"; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/31-advanced-strings/110-locales/SolutionTest.php b/modules/31-advanced-strings/110-locales/SolutionTest.php index 358fc408..2115dabf 100644 --- a/modules/31-advanced-strings/110-locales/SolutionTest.php +++ b/modules/31-advanced-strings/110-locales/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = setlocale(LC_CTYPE, 0); - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/31-advanced-strings/120-startwith/SolutionTest.php b/modules/31-advanced-strings/120-startwith/SolutionTest.php index 1c62e406..49e2cae1 100644 --- a/modules/31-advanced-strings/120-startwith/SolutionTest.php +++ b/modules/31-advanced-strings/120-startwith/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = '18'; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/31-advanced-strings/25-interpolation/SolutionTest.php b/modules/31-advanced-strings/25-interpolation/SolutionTest.php index 1be5d35f..28e9474a 100644 --- a/modules/31-advanced-strings/25-interpolation/SolutionTest.php +++ b/modules/31-advanced-strings/25-interpolation/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = 'Здравствуйте, Анна! Ваш заказ #1337 принят.'; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/31-advanced-strings/30-symbols/SolutionTest.php b/modules/31-advanced-strings/30-symbols/SolutionTest.php index f94cbb73..1f12cd25 100644 --- a/modules/31-advanced-strings/30-symbols/SolutionTest.php +++ b/modules/31-advanced-strings/30-symbols/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = 'grip'; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/31-advanced-strings/70-substrings/SolutionTest.php b/modules/31-advanced-strings/70-substrings/SolutionTest.php index 28f2201c..f570616e 100644 --- a/modules/31-advanced-strings/70-substrings/SolutionTest.php +++ b/modules/31-advanced-strings/70-substrings/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = 'hexlet.io'; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/31-advanced-strings/90-multiline-strings/SolutionTest.php b/modules/31-advanced-strings/90-multiline-strings/SolutionTest.php index 094b72d9..1d90bd42 100644 --- a/modules/31-advanced-strings/90-multiline-strings/SolutionTest.php +++ b/modules/31-advanced-strings/90-multiline-strings/SolutionTest.php @@ -14,6 +14,6 @@ public function test() Ожидаемая дата доставки: 3-5 рабочих дней. Спасибо, что выбрали нас! EOT; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/33-data-types/40-primitive-data-types/SolutionTest.php b/modules/33-data-types/40-primitive-data-types/SolutionTest.php index 6d27cb11..119590e2 100644 --- a/modules/33-data-types/40-primitive-data-types/SolutionTest.php +++ b/modules/33-data-types/40-primitive-data-types/SolutionTest.php @@ -15,6 +15,6 @@ public function test() Rating: 4.7 HERE; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/33-data-types/50-data-types-weak-typing/SolutionTest.php b/modules/33-data-types/50-data-types-weak-typing/SolutionTest.php index c3a89d76..4c896844 100644 --- a/modules/33-data-types/50-data-types-weak-typing/SolutionTest.php +++ b/modules/33-data-types/50-data-types-weak-typing/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = '13'; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/33-data-types/52-data-types-immutability/SolutionTest.php b/modules/33-data-types/52-data-types-immutability/SolutionTest.php index 3239872b..4f61889d 100644 --- a/modules/33-data-types/52-data-types-immutability/SolutionTest.php +++ b/modules/33-data-types/52-data-types-immutability/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = 'print'; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/33-data-types/55-data-types-casting/SolutionTest.php b/modules/33-data-types/55-data-types-casting/SolutionTest.php index 814092d6..d1d6ee82 100644 --- a/modules/33-data-types/55-data-types-casting/SolutionTest.php +++ b/modules/33-data-types/55-data-types-casting/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = '36 °C'; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/35-calling-functions/100-call/SolutionTest.php b/modules/35-calling-functions/100-call/SolutionTest.php index 1b276e41..6b423923 100644 --- a/modules/35-calling-functions/100-call/SolutionTest.php +++ b/modules/35-calling-functions/100-call/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = "12"; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/35-calling-functions/135-calling-functions-default-params/SolutionTest.php b/modules/35-calling-functions/135-calling-functions-default-params/SolutionTest.php index 3e48af8e..1de5253f 100644 --- a/modules/35-calling-functions/135-calling-functions-default-params/SolutionTest.php +++ b/modules/35-calling-functions/135-calling-functions-default-params/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = "37.8\n2426.76\n607\n"; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/35-calling-functions/150-calling-functions-expression/SolutionTest.php b/modules/35-calling-functions/150-calling-functions-expression/SolutionTest.php index a95251eb..84272e95 100644 --- a/modules/35-calling-functions/150-calling-functions-expression/SolutionTest.php +++ b/modules/35-calling-functions/150-calling-functions-expression/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = "First: H\nLast: !"; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/35-calling-functions/180-variadic-parameters/SolutionTest.php b/modules/35-calling-functions/180-variadic-parameters/SolutionTest.php index 5cb4d1c2..fb534919 100644 --- a/modules/35-calling-functions/180-variadic-parameters/SolutionTest.php +++ b/modules/35-calling-functions/180-variadic-parameters/SolutionTest.php @@ -9,6 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = '-3'; - $this->expectOutputString($expected); + $this->assertOutput($expected); } } diff --git a/modules/35-calling-functions/270-deterministic/SolutionTest.php b/modules/35-calling-functions/270-deterministic/SolutionTest.php index 8e3b179d..b9463a4e 100644 --- a/modules/35-calling-functions/270-deterministic/SolutionTest.php +++ b/modules/35-calling-functions/270-deterministic/SolutionTest.php @@ -8,7 +8,7 @@ class SolutionTest extends TestCase { public function test() { - $randomRoll = $this->getActualOutputForAssertion(); + $randomRoll = $this->solutionOutput; $this->assertThat( $randomRoll, $this->logicalAnd( diff --git a/modules/40-define-functions/100-define-function/SolutionTest.php b/modules/40-define-functions/100-define-function/SolutionTest.php index 887bfae6..760273e7 100644 --- a/modules/40-define-functions/100-define-function/SolutionTest.php +++ b/modules/40-define-functions/100-define-function/SolutionTest.php @@ -9,7 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = 'Hello, World!'; - $this->expectOutputString($expected); - sayHello(); + $this->assertOutputOf(fn () => sayHello(), $expected); } } diff --git a/modules/50-loops/10-while/SolutionTest.php b/modules/50-loops/10-while/SolutionTest.php index e92c2077..870166af 100644 --- a/modules/50-loops/10-while/SolutionTest.php +++ b/modules/50-loops/10-while/SolutionTest.php @@ -9,7 +9,6 @@ class SolutionTest extends TestCase public function test() { $expected = "4\n3\n2\n1\nGo!"; - $this->expectOutputString($expected); - printCountdown(4); + $this->assertOutputOf(fn () => printCountdown(4), $expected); } } diff --git a/src/TestCase.php b/src/TestCase.php index 6b7a8411..24d12ad0 100644 --- a/src/TestCase.php +++ b/src/TestCase.php @@ -7,11 +7,38 @@ abstract class TestCase extends BaseTestCase { + protected string $solutionOutput = ''; + protected function setUp(): void { parent::setUp(); $solutionPath = dirname((new ReflectionObject($this))->getFileName()) . '/Solution.php'; + + ob_start(); require $solutionPath; + $this->solutionOutput = (string) ob_get_clean(); + + if ($this->solutionOutput !== '') { + fwrite(STDOUT, $this->solutionOutput . "\n"); + } + } + + protected function assertOutput(string $expected): void + { + $this->assertSame($expected, $this->solutionOutput); + } + + protected function assertOutputOf(callable $callback, string $expected): void + { + ob_start(); + $callback(); + $output = (string) ob_get_clean(); + + if ($output !== '') { + fwrite(STDOUT, $output . "\n"); + } + + $this->assertSame($expected, $output); } } From 57ec079a773223a584c078e122546d1254aa3b7c Mon Sep 17 00:00:00 2001 From: Nikolay Gagarinov Date: Thu, 2 Jul 2026 19:03:04 +0500 Subject: [PATCH 2/2] =?UTF-8?q?docs:=20PHPDoc=20=D0=BA=20=D0=BC=D0=B5?= =?UTF-8?q?=D1=82=D0=BE=D0=B4=D0=B0=D0=BC=20=D0=B8=20=D1=81=D0=B2=D0=BE?= =?UTF-8?q?=D0=B9=D1=81=D1=82=D0=B2=D1=83=20=D0=B1=D0=B0=D0=B7=D0=BE=D0=B2?= =?UTF-8?q?=D0=BE=D0=B3=D0=BE=20TestCase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Документированы назначение класса, $solutionOutput, setUp() (перехват вывода решения и печать его на панель через fwrite(STDOUT)), assertOutput() и assertOutputOf() — с акцентом на неочевидную механику вывода на панель. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/TestCase.php | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/TestCase.php b/src/TestCase.php index 24d12ad0..81f1e46e 100644 --- a/src/TestCase.php +++ b/src/TestCase.php @@ -5,10 +5,30 @@ use PHPUnit\Framework\TestCase as BaseTestCase; use ReflectionObject; +/** + * Базовый класс тестов упражнений. + * + * Подключает решение студента (Solution.php) и следит за тем, чтобы напечатанный + * им результат попадал на панель «вывод». Панель показывает дословный stdout + * команды `make test`, поэтому вывод решения нужно не только сверить с ожидаемым, + * но и явно вернуть в реальный stdout — в обход буферизации PHPUnit. + */ abstract class TestCase extends BaseTestCase { + /** + * Вывод, напечатанный решением на верхнем уровне при подключении Solution.php. + */ protected string $solutionOutput = ''; + /** + * Подключает решение студента и перехватывает его вывод. + * + * require Solution.php оборачивается в собственный буфер вывода: напечатанное + * сохраняется в {@see self::$solutionOutput} и сразу же печатается в настоящий + * stdout через fwrite(STDOUT, ...), чтобы студент увидел результат на панели + * «вывод». Перехват идёт в отдельный буфер (а не в буфер PHPUnit) — иначе + * PHPUnit счёл бы вывод «рискованным» при failOnRisky. + */ protected function setUp(): void { parent::setUp(); @@ -24,11 +44,29 @@ protected function setUp(): void } } + /** + * Сверяет с ожидаемым вывод, напечатанный решением на верхнем уровне Solution.php. + * + * Подходит для большинства уроков, где решение печатает результат сразу при + * подключении файла (вывод уже перехвачен в {@see self::setUp()}). + * + * @param string $expected Ожидаемый вывод программы студента + */ protected function assertOutput(string $expected): void { $this->assertSame($expected, $this->solutionOutput); } + /** + * Сверяет с ожидаемым вывод, который решение печатает при вызове функции. + * + * Нужен для уроков, где Solution.php лишь ОБЪЯВЛЯЕТ функцию, а печать + * происходит при её вызове из теста (а не при подключении файла). Перехватывает + * вывод переданного вызова, печатает его на панель «вывод» и сверяет с ожидаемым. + * + * @param callable $callback Вызов решения, печатающий результат (например, fn () => sayHello()) + * @param string $expected Ожидаемый вывод + */ protected function assertOutputOf(callable $callback, string $expected): void { ob_start();