Skip to content

Commit 094187c

Browse files
authored
Adding a fail() command to tests, fixing command testing for failed commands (#643)
* Adding a fail() command to tests, fixing command testing for failed commands * Indent * CHANGELOG * Match it up
1 parent e6e6f64 commit 094187c

File tree

5 files changed

+95
-16
lines changed

5 files changed

+95
-16
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111

1212
- Added a `Option` support class and `option()` helper to retrieve options in a type-safe manner.
13+
- Added `fail()` method to commands to allow for a command to fail with a message.
14+
15+
### Fixed
16+
17+
- Fixed command testing to proper handle failed commands.
1318

1419
## v1.5.4 - 2025-03-06
1520

src/mantle/console/class-command.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Component\Console\Input\InputInterface;
1515
use Symfony\Component\Console\Output\ConsoleOutput;
1616
use Symfony\Component\Console\Output\OutputInterface;
17+
use Throwable;
1718

1819
/**
1920
* CLI Command for Service Providers
@@ -186,8 +187,28 @@ public function set_container( \Mantle\Contracts\Application $container ): void
186187
}
187188

188189
/**
189-
* Retrieve the application container. */
190+
* Retrieve the application container.
191+
*/
190192
public function get_container(): \Mantle\Contracts\Application {
191193
return $this->container;
192194
}
195+
196+
/**
197+
* Fail the command.
198+
*
199+
* @param Throwable|string|null $exception Exception to throw.
200+
*
201+
* @throws Manually_Failed_Exception|Throwable Thrown exception.
202+
*/
203+
public function fail( Throwable|string|null $exception = null ): void {
204+
if ( is_null( $exception ) ) {
205+
$exception = 'Command manually failed.';
206+
}
207+
208+
if ( is_string( $exception ) ) {
209+
$exception = new Manually_Failed_Exception( $exception );
210+
}
211+
212+
throw $exception;
213+
}
193214
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
/**
3+
* Manually_Failed_Exception class file
4+
*
5+
* @package Mantle
6+
*/
7+
8+
namespace Mantle\Console;
9+
10+
use RuntimeException;
11+
12+
/**
13+
* Manually Failed Command Exception
14+
*/
15+
class Manually_Failed_Exception extends RuntimeException {}

src/mantle/testing/class-test-command.php

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
use Symfony\Component\Console\Tester\CommandTester;
1717
use PHPUnit\Framework\Assert;
1818
use Symfony\Component\Console\Exception\CommandNotFoundException;
19+
use Symfony\Component\Console\Exception\ExceptionInterface;
20+
use Throwable;
1921

2022
/**
2123
* Faux "PendingCommand" class for unit testing.
@@ -28,6 +30,11 @@ class Test_Command {
2830
*/
2931
protected CommandTester $tester;
3032

33+
/**
34+
* Exception thrown during command execution.
35+
*/
36+
protected Throwable $exception;
37+
3138
/**
3239
* Flag if the command has been executed.
3340
*/
@@ -139,6 +146,10 @@ public function dd(): never {
139146
$this->run();
140147
}
141148

149+
if ( isset( $this->exception ) ) {
150+
dd( $this->exception );
151+
}
152+
142153
dd( $this->tester->getDisplay() );
143154
}
144155

@@ -179,7 +190,7 @@ protected function verify_command(): void {
179190
}
180191

181192
/**
182-
* Run the command.
193+
* Run the command and verify the expectations.
183194
*/
184195
public function run(): static {
185196
$this->has_executed = true;
@@ -188,8 +199,8 @@ public function run(): static {
188199
$this->tester = $this->app->make(
189200
\Mantle\Framework\Console\Kernel::class
190201
)->test( $this->command, $this->arguments );
191-
} catch ( CommandNotFoundException ) {
192-
$this->test->fail( "Command [{$this->command}] not found." );
202+
} catch ( Throwable $e ) {
203+
$this->exception = $e;
193204
}
194205

195206
$this->verify_expectations();
@@ -203,7 +214,7 @@ public function run(): static {
203214
protected function verify_expectations(): void {
204215
// Assert that the exit code matches the expected exit code.
205216
if ( null !== $this->expected_exit_code ) {
206-
$exit_code = $this->tester->getStatusCode();
217+
$exit_code = $this->get_status_code();
207218

208219
$this->test->assertEquals(
209220
$this->expected_exit_code,
@@ -213,29 +224,20 @@ protected function verify_expectations(): void {
213224
}
214225

215226
if ( ! empty( $this->expected_output ) ) {
216-
$output = $this->tester->getDisplay();
227+
$output = $this->get_output();
217228

218229
foreach ( $this->expected_output as $expected_output ) {
219230
$this->test->assertStringContainsString( $expected_output, $output );
220231
}
221232
}
222233

223234
if ( ! empty( $this->unexpected_output ) ) {
224-
$output = $this->tester->getDisplay();
235+
$output = $this->get_output();
225236

226237
foreach ( $this->unexpected_output as $unexpected_output ) {
227238
$this->test->assertStringNotContainsString( $unexpected_output, $output );
228239
}
229240
}
230-
231-
// todo: add output substring assertions.
232-
// if ( ! empty( $this->expected_output_substrings ) ) {
233-
// $output = $this->tester->getDisplay();
234-
235-
// foreach ( $this->expected_output_substrings as $expected_output_substring ) {
236-
// $this->test->assertStringContainsString( $expected_output_substring, $output );
237-
// }
238-
// }
239241
}
240242

241243
/**
@@ -246,4 +248,26 @@ public function __destruct() {
246248
$this->run();
247249
}
248250
}
251+
252+
/**
253+
* Retrieve the output of the command.
254+
*/
255+
public function get_output(): string {
256+
return match ( true ) {
257+
isset( $this->exception ) => $this->exception->getMessage(),
258+
isset( $this->tester ) => $this->tester->getDisplay(),
259+
default => '',
260+
};
261+
}
262+
263+
/**
264+
* Retrieve the status code of the command.
265+
*/
266+
public function get_status_code(): int {
267+
return match ( true ) {
268+
isset( $this->exception ) => Command::FAILURE,
269+
isset( $this->tester ) => $this->tester->getStatusCode(),
270+
default => Command::FAILURE,
271+
};
272+
}
249273
}

tests/Testing/Concerns/InteractsWithConsoleTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,20 @@ public function test_list_command() {
2929
->assertOk();
3030
}
3131

32+
public function test_command_failure(): void {
33+
Console::command( 'fail', fn () => $this->fail() );
34+
35+
$this->command( 'wp mantle fail' )
36+
->assertOutputContains( 'Command manually failed' )
37+
->assertFailed();
38+
39+
Console::command( 'fail:message', fn () => $this->fail( 'With message' ) );
40+
41+
$this->command( 'wp mantle fail:message' )
42+
->assertOutputContains( 'With message' )
43+
->assertFailed();
44+
}
45+
3246
public function test_closure_command() {
3347
Console::command( 'hello-world', fn () => $this->info( 'Hello World' ) )
3448
->describe( 'Command description' );

0 commit comments

Comments
 (0)