I was writing a very basic test for a Laravel Artisan Console Command, like this:
$this->artisan("my-command", ["--some-option" => "some-value"])
->expectsOutput("the expected output");
The test didn't pass. I got really troubled because "the expected output" was exactly what the command was outputting when executed mannualy.
But that's ok, I just have to inspect what the command output actually is when executed through automated tests, right? But wait, how do I do that?
I tried the following:
$output = new BufferedConsoleOutput();
Artisan::call("my-command", ["--some-option", "some-value"], $output);
// dd($output->fetch()
$this->assertTrue($output->fetch() === "the expected output");
But $output->fetch() seems to be always empty.
In brief: How do I print the actual output of a Laravel command in the context of a test?
This isn't something I would want to set up and leave in your tests, but if you prevent Laravel from mocking the console output, you can capture it for inspection.
Assuming you have this test that fails:
public function testStuffDoesntBreak(): void
{
$this->artisan("my-command", ["--some-option" => "some-value"])
->expectsOutput("the expected output");
}
You can rewrite it to this:
use Illuminate\Support\Facades\Artisan;
...
public function testStuffDoesntBreak(): void
{
$this->withoutMockingConsoleOutput()
->artisan("my-command", ["--some-option" => "some-value"]);
// capture the text output from the command
$result = Artisan::output();
// use standard text assertions
$this->assertEquals("the expected output", $result);
}
When you've disabled console mocking, the artisan() method stops being fluent and instead returns the exit code of the command. But it does allow the Artisan facade to access its output. Whether you want to rewrite tests or just change them on the fly in case of an error is personal preference. I've done the latter, as I'd rather not miss out on features like expectsTable().
Related
Problem / What I've tried:
Getting the currently used controller and action in Laravel 5 is easy (but not as easy as it should be), however I'm stuck with getting the currently used artisan console command.
To fetch the controller name I do this:
$route = Route::getRoutes()->match(Request::capture());
$listAction = explode('\\', $route->getActionName());
$rawAction = end($listAction);
// controller name and action in a simple array
$controllerAndAction = explode('#', $rawAction);
But when calling from a console action, it always returns the default index controller's name ("IndexController" or so in Laravel). Does anybody know how to make this ?
By the way I've also worked throught Request::capture() but this still gives no info about the command.
The simplest way is to just to look at the arguments specified on the command line:
if (array_get(request()->server(), 'argv.1') === 'cache:clear') {
// do things
}
Yes, you can use $_SERVER directly, but I like to use the helper functions or the Facades, as those will give you the current data.
I go from the assumption that - during unit tests - the superglobals might not always reflect the currently tested request.
By the way: Obviously can also do array_get(request()->server('argv'), '1') or something alike. (request()->server('argv.1') doesnt work at this point). Or use \Request::server(). Depends on what you like most.
As per the Symfony\Component\Console\Command\Command class, the method to return the name of the command (eg. my:command) is:
$this->getName();
You should use it from within an Artisan command extending Illuminate\Console\Command (default on Artisan commands).
Remember that it will return only the command name and not the available parameters (eg. for the command signature my:command {--with-params=} it will only return my:command).
Reflection might be of help? Try this:
$var = new \ReflectionClass($this);
dd($var);
I can see if a script is running in the console with the App::runningInConsole() command, but I would like to know (for audit logging purposes) which command has been run from console.
To add some context - I need to log whenever a system or user accesses a certain type of data. For users, that's pretty simple - I can use Auth::user() and get their IP address from Request, but for console commands, it's a little more difficult.
I can figure out if a console command is running, which is good, but I need to be able to record which console command is running.
I do not see a way to get that information directly Laravel. I'm also not sure how that would be possible, as you can have one command executing other commands or even creating a new application instance like some test tools do.
There is however a way to achieve that using plain PHP. You can check the server/environment variables to identify how application was executed.
Have a look at
dd($_SERVER['argv']);
If you run a command this should give you the command name in $_SERVER['argv'][1]
You can find more information here: http://php.net/manual/en/reserved.variables.argv.php
To get console command together with arguments you should use something like that:
$command = \Request::server('argv', null);
if (is_array($command)) {
$command = implode(' ', $command);
}
In Laravel you can use \Request:server to get $_SERVER variables.
The problem with $SERVER['arg'] is that it doesnt work when you execute the command on the GUI.
I fixed the problem adding the next code on the command.
private function getAttributes()
{
$arguments = $this->arguments();
unset($arguments[0]);
$arguments = collect($arguments)->implode(' ');
$options = collect($this->options())->filter(function($item) {
return $item;
})->map(function($item, $key) {
$return = '--'.$key;
if($item!==true) {
$return .= '='.$item;
}
return $return;
})->implode(' ');
return $arguments.' '.$options;
}
and you get it calling $this->getAttributes() and you will get all the command with the attributes
I have several integration tests with phpunit,
and in the proccess of the tests there are some logs written to files in the system.
I would like to check if a line was written during a test, is that possible?
example:
/** #test */
function action_that_writes_to_log() {
$this->call('GET', 'path/to/action', [], [], $requestXml);
//I want this:
$this->assertFileHas('the log line written', '/log/file/path.log');
}
The obvious way:
Implementing a custom assertion method, like the one you propose: assertFileHas. It's quite easy, just check if the string appears in the file. The problem you can get is that the line can already exist from another test or the same test already run. A possible solution for this is deleting the logs content before each test or test class, depending on your needs. You would need a method that deletes the logs and call it from setUp or setUpBeforeClass.
I would go with another approach: mocking the logging component, and checking that the right call is being done:
$logger_mock->expects($this->once())
->method('log')
->with($this->equalTo('the log line written'));
This makes easy to test that the components are logging the right messages, but you also need to implement a test that verifies that the logger is capable of actually writting to the file. But it's easier to implement that test once, and then just check that each component calls the logging method.
I have a couple of classes (services) which I use in the main Laravel app as well as via artisan commands.
What I would like to do is being able to display some output when invoking such class methods via artisan (as $this->info(...) inside the command itself) and just eventually return a value (response, result object or similar) when using them within the main app.
Right now I just used them as commands and achieved the output effect by echoing the statements I needed directly from within the methods, but I'm convinced this is not the correct approach because as soon as I start using them in the app I guess I'm gonna have my responses polluted with these info statements (as well as double headers or similar issues, I imagine).
I wonder what would be a better way to approach such scenario.
Below a code example of what I'm trying to achieve:
// DoSequenceCommand
public function fire() {
MyClass::doSequence();
}
// MyClass (Facade)
public function doSequence() {
echo "Beginning Step 1\n"; // THIS I WOULD BE ABLE TO OUTPUT ONLY IF EXECUTED VIA COMMAND LINE
$s1 = $this->step1();
echo "Step 1 " . ( $s1 ? "succeded" : "failed" ) . "!\n";
echo "Beginning Step 2\n";
$s2 = $this->step2();
echo "Step 2 " . ( $s2 ? "succeded" : "failed" ) . "!\n";
return [ "s1" => $s1, "s2" => $s2];
}
You could either:
pass a flag into doSequence() to determine if it should output info statements
fire events in doSequenence() and listen for them in your command to output info from there
break out the sequence into multiple functions, and call them independently in your command, along with your info statements
The events option might be the cleanest way.
I would like to display processing progress using a simple series of dots. This is easy in the browser, just do echo '.' and it goes on the same line, but how do I do this on the same line when sending data to the artisan commandline?
Each subsequent call to $this->info('.') puts the dot on a new line.
The method info uses writeln, it adds a newline at the end, you need to use write instead.
//in your command
$this->output->write('my inline message', false);
$this->output->write('my inline message continues', false);
Probably a little bit of topic, since you want a series of dots only. But you can easily present a progress bar in artisan commands using built in functionality in Laravel.
Declare a class variable like this:
protected $progressbar;
And initialize the progress bar like this, lets say in fire() method:
$this->progressbar = $this->getHelperSet()->get('progress');
$this->progressbar->start($this->output, Model::count());
And then do something like this:
foreach (Model::all() as $instance)
{
$this->progressbar->advance(); //do stuff before or after this
}
And finilize the progress when done by calling this:
$this->progressbar->finish();
Update: For Laravel 5.1+ The simpler syntax is even more convenient:
Initialize $bar = $this->output->createProgressBar(count($foo));
Advance $bar->advance();
Finish $bar->finish();
If you look at the source, you will see that $this->info is actually just a shortcut for $this->output->writeln: Source.
You could use $this->output->write('<info>.</info>') to make it inline.
If you find yourself using this often you can make your own helper method like:
public function inlineInfo($string)
{
$this->output->write("<info>$string</info>");
}