Testing console command that uses kernel.terminate event (KernelTestCase) - symfony

I want to test a symfony 4 console command.
Importantly, I want to test the situation after all kernel.terminate eventlisteners have finished. To demonstrate here, I have a most simple listener that var_dumps a string:
class NotificationCenter implements EventSubscriberInterface
{
public function onException()
{
$this->wasExceptionThrown = true;
}
public function onTerminate()
{
if ($this->wasExceptionThrown) {
return;
}
var_dump("Hiho from terminate");
}
public static function getSubscribedEvents()
{
$listeners = [
KernelEvents::EXCEPTION => ['onException', 1024],
KernelEvents::TERMINATE => ['onTerminate', 1024],
];
if (class_exists('Symfony\Component\Console\ConsoleEvents')) {
$listeners[class_exists('Symfony\Component\Console\Event\ConsoleErrorEvent') ? ConsoleEvents::ERROR : ConsoleEvents::EXCEPTION] = ['onException', 1024];
$listeners[ConsoleEvents::TERMINATE] = ['onTerminate', 1024];
}
return $listeners;
}
public function reset()
{
$this->wasExceptionThrown = false;
}
}
So, with this eventlistener enabled, any call to any command (or http route) outputs this string.
According to the docs, I have created a Test case:
class MissingDataNotifyCommandTest extends KernelTestCase
{
protected function setUp()
{
self::bootKernel();
}
public function testHiHoIsThere()
{
$application = new Application(static::$kernel);
$command = $application->find('debug:event-dispatcher');
$commandTester = new CommandTester($command);
$commandTester->execute(['command' => $command->getName()]);
$this->assertContains('Hiho from terminate', $commandTester->getDisplay());
}
}
But the assertion fails. Funny enough, the debug command that var_dumps is listed under kernel.terminate.
How can I make sure the kernel.terminate eventlisteners are dispatched during a KernelTestCase?
Edit: my solution
Ok, with Tomas Votruba's help, I was able to find a (partial) solution:
class MissingDataNotifyCommandTest extends KernelTestCase
{
protected function setUp()
{
self::bootKernel();
}
public function testHiHoIsThere()
{
$application = new Application(static::$kernel);
$application->setAutoExit(false);
$application->setCatchExceptions(false);
$stringInput = ['debug:event-dispatcher'];
$input = new StringInput(implode(' ', $stringInput));
$application->run($input, new NullOutput());
}
}
The only problem I could not solve was to get the output of the command back into my code. Moreover, this somehow does run some framework/container code twice (once when booting the kernel inside the tests, once when executing the command).

I double checked and it looks like you're testing Console, not Kernel. They're 2 different classes.
Kernel terminate event is invoked here.
Console Application "terminate" event is invoked here.
Try adding console terminate event to subscriber - console.terminate.
class YourSubscriber implements EventSubccriberInterface
{
public static function getSubscribedEvents()
{
return [
KernelEvents::TERMINATE => 'someFunction',
ConsoleEvents::TERMINATE => 'someFunction',
];
}
public function someFunction()
{
}
}

Related

PHPUnit 5.1.3: assertEquals() not working. Error: No tests executed

I need to compare two arrays in phpunit. I am using asserequals() but output shows No Tests Executed! I am mentioning my arrays below and code too:
$expected_arr = array('success' => 1);
$result_array =(output of print_r($result_array))
Array
(
[success] => 1
)
$this->assertEquals($arr_data,$expected_arr);
My PHPUnit version is 5.1.3. I am running is on Ubuntu 16
UPDATED:
<?php
require_once ('PHPUnit/Framework/TestCase.php');
class abc_auto_testing_test extends PHPUnit_Framework_TestCase
{
public $abc_id;
public $abc_answer;
public function __construct($ABC_id, $ABC_answer)
{
$this->abc_id = $ABC_id;
$this->abc_answer = $ABC_answer;
$this->test_Abc_Validate($this->abc_id, $this->abc_answer);
}
public function setUp()
{
}
public function tearDown()
{
}
public function test_Abc_Validate($abcId, $abcAnswer)
{
$expected_arr = array('success' => 1);
// var_dump($expected_arr);
$arr_data = ABC_Validate($abcId, $abcAnswer);
// var_dump($arr_data);
$this->assertEquals($arr_data,$expected_arr);
}
}
require'/var/www/data.abc.in/abc_server_crons/abc_auto_testing_bkp.php';
$ABC_identifier = $abcIdentifier;
$ABC_answer = $abcAnswer;
$validObj = new abc_auto_testing_test($ABC_identifier, $ABC_answer);
?>
Your test code makes no sense. Go here to learn the basics of writing and running tests.

Laravel 4 Model Events don't work with PHPUnit

I build a model side validation in Laravel 4 with the creating Model Event :
class User extends Eloquent {
public function isValid()
{
return Validator::make($this->toArray(), array('name' => 'required'))->passes();
}
public static function boot()
{
parent::boot();
static::creating(function($user)
{
echo "Hello";
if (!$user->isValid()) return false;
});
}
}
It works well but I have issues with PHPUnit. The two following tests are exactly the same but juste the first one pass :
class UserTest extends TestCase {
public function testSaveUserWithoutName()
{
$count = User::all()->count();
$user = new User;
$saving = $user->save();
assertFalse($saving); // pass
assertEquals($count, User::all()->count()); // pass
}
public function testSaveUserWithoutNameBis()
{
$count = User::all()->count();
$user = new User;
$saving = $user->save();
assertFalse($saving); // fail
assertEquals($count, User::all()->count()); // fail, the user is created
}
}
If I try to create a user twice in the same test, it works, but it's like if the binding event is present only in the first test of my test class. The echo "Hello"; is printed only one time, during the first test execution.
I simplify the case for my question but you can see the problem : I can't test several validation rules in different unit tests. I try almost everything since hours but I'm near to jump out the windows now ! Any idea ?
The issue is well documented in Github. See comments above that explains it further.
I've modified one of the 'solutions' in Github to automatically reset all model events during the tests. Add the following to your TestCase.php file.
app/tests/TestCase.php
public function setUp()
{
parent::setUp();
$this->resetEvents();
}
private function resetEvents()
{
// Get all models in the Model directory
$pathToModels = '/app/models'; // <- Change this to your model directory
$files = File::files($pathToModels);
// Remove the directory name and the .php from the filename
$files = str_replace($pathToModels.'/', '', $files);
$files = str_replace('.php', '', $files);
// Remove "BaseModel" as we dont want to boot that moodel
if(($key = array_search('BaseModel', $files)) !== false) {
unset($files[$key]);
}
// Reset each model event listeners.
foreach ($files as $model) {
// Flush any existing listeners.
call_user_func(array($model, 'flushEventListeners'));
// Reregister them.
call_user_func(array($model, 'boot'));
}
}
I have my models in subdirectories so I edited #TheShiftExchange code a bit
//Get all models in the Model directory
$pathToModels = '/path/to/app/models';
$files = File::allFiles($pathToModels);
foreach ($files as $file) {
$fileName = $file->getFileName();
if (!ends_with($fileName, 'Search.php') && !starts_with($fileName, 'Base')) {
$model = str_replace('.php', '', $fileName);
// Flush any existing listeners.
call_user_func(array($model, 'flushEventListeners'));
// Re-register them.
call_user_func(array($model, 'boot'));
}
}

WebFlowTestCase registerFlow() multiple flows possible?

Hi I have a WebFlowTestCase and is working fine but I ran into a prob when I need to test another flow that is in the same controller ( groovy).
The is what my controller looks like:
class MyController {
def someService
def dateHelper = new DateHelper()
def index = {... }
def myCreateFlow = {
start{}
createCase{}
finishCancel{
}
def myViewFlow = {...}
def myEditFlow = {...}
}
I have managed to successfully create the test for myCreateFlow like this:
class MyControllerTest extends WebFlowTestCase {
def myController = new MyController();
#Override
public Object getFlow() {
// TODO Auto-generated method stub
return myController.myCreateFlow
}
protected void setUp() {
super.setUp()
}
protected void tearDown() {
super.tearDown()
}
void testmyCreateFlow()
{
...
}
}
my question is how about the myEditFlow and myViewFlow? How do I register or use it when the getFlow() returns only the myCreateFlow? Is there I way I can use all of them in one webflowtest with out creating a new webflowtestclass? Or is there a way I can put it inside getflow with some switch/if else method something like:
#Override
public Object getFlow() {
// TODO Auto-generated method stub
if condition
return myController.myCreateFlow
else return myController.myEditFlow
}
coz when i tried creating a testmyEditFlow() I get the error below and I know that it is because the get flow only returns the myCreateFlow. At least that is how I perceive the test error msg.
Cannot find state with id 'myEditFlow' in flow 'test' -- Known state
ids are 'array['start', 'createCase'... 'finishCancel']'
You could register your other flows in a setUp method as follows:
protected void setUp() {
super.setUp()
registerFlow("myController/myEdit", myController.myEditFlow)
registerFlow("myController/myView", myController.myViewFlow)
}

PureMVC / complicated asMock

Ok - I've got a bit of a complicated asMock setup here; I've got a PureMVC async command that is attempting to call another class that implements interfaces in order to set up some asmocks for development without the backend.
import test.mix.common.business.MockInterbahnServiceFactory;
public class InitMockInterbahnServiceFactory extends AsyncCommand{
public static var mockServiceFactory:MockInterbahnServiceFactory = new MockInterbahnServiceFactory();
override public function execute(notification:INotification):void{
var serviceResult:IEventDispatcher = mockServiceFactory.mockRepository.prepare([EchoBusinessObjects, SendBusinessObjects]);
//serviceResult.addEventListener(Event.COMPLETE, onComplete);
}
private function onComplete(event:Event):void{
mx.controls.Alert.show("COMPLETE!");
var logMessage:String = "4 MOCK SERVICE FACTORY MOCKED !!!!!";
sendNotification( MixConstants.LOG_OUTPUT, logMessage );
//sendNotification(MixConstants.INTERBAHN_CONNECTED, mockServiceFactory);
// commandComplete() ;
}
}
This is actually trying to set up a MockRepositoryFactory:
public class MockInterbahnServiceFactory implements ServiceFactory
{
[Mock] public static var withMocks : Array = [
SendBusinessObjects, EchoBusinessObjects
];
//public static var mockRepository:MockRepository ;//= new MockRepository();
public var mockSendBusinessObjects:SendBusinessObjects;
public var mockEchoBusinessObjects:EchoBusinessObjects ;
public var mockRepository:MockRepository;
public function MockInterbahnServiceFactory(){
mockRepository = new MockRepository();
prepareMocks();
}
public function prepareMocks():void{
var prepareDispatcher:IEventDispatcher = mockRepository.prepare([SendBusinessObjects, EchoBusinessObjects]);
prepareDispatcher.addEventListener(Event.COMPLETE, setupMocks);
}
public function setupMocks(event:Event):void{
mockSendBusinessObjects = SendBusinessObjects(mockRepository.create(SendBusinessObjects));
mockEchoBusinessObjects = EchoBusinessObjects(mockRepository.create(EchoBusinessObjects));
SetupResult.forCall(mockSendBusinessObjects.sendOrder(new Order())).returnValue('wee');
}
public function createSendBusinessObjectService():SendBusinessObjects{
return mockSendBusinessObjects;
}
public function createEchoBusinessObjectService():EchoBusinessObjects{
return mockEchoBusinessObjects;
}
}
}
And at some point this factory is going to get passed around and utilized for the send / receive endpoints for multiple communications (true backend being a scala one).
I'm getting this error:
ArgumentError: returnValue must be assignable from :void
at asmock.framework.expectations::AbstractExpectation/set returnValue()[C:\Users\Richard\SVN\asmock\trunk\source\ASMock\src\asmock\framework\expectations\AbstractExpectation.as:107]
at asmock.framework::MethodOptions/returnValue()[C:\Users\Richard\SVN\asmock\trunk\source\ASMock\src\asmock\framework\MethodOptions.as:134]
at test.mix.common.business::MockInterbahnServiceFactory/setupMocks()[/Users/grimm/Documents/__WORK/__INVESTLAB/MIX/trunk/src/test/mix/common/business/MockInterbahnServiceFactory.as:56]
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at Function/org.floxy:ProxyRepository/org.floxy:IProxyRepository:prepare/org.floxy:swfLoadedHandler()[C:\transfer\IdeaProjects\as3-interbahn\floxy\main\as3\src\org\floxy\ProxyRepository.as:218]
I'm assuming this is because of the interface functions I'm stubbing?
public interface SendBusinessObjects {
function sendFirmExchangePermission(frp:FirmExchangePermission):void ;
function sendFirm(f:Firm):void ;
function sendExchange(ex:Exchange):void ;
function sendFXConversion(fx:FXConversion):void ;
function sendInstrument(ins:Instrument):void ;
function sendQuote(q:Quote):void ;
It looks to me like SendBusinessObjects returns void, but you are calling returnValue when you are mocking the call to it. Remove the returnValue('wee') call and it should work as expected.

Actionscript 3 Null Object Error Message

I am building a AS3 only project and got runtime error that said "Cannot access a property or method of a null object reference."
Here is my code:
main.as
public class videoMain extends Sprite{
private var videoPlayer:Player;
public function videoMain (){
loadPlayer()
loadProgress();
}
private function loadProgress():void{
//the code below gave me null object error.....
var byteLoaded:Number=videoPlayer.videoBytesLoaded; //the problem code
var byteTotal:Number=videoPlayer.videoBytesTotal; //the problem code
var percent:Number=Math.floor(byteLoaded/byteTotal)*100;
}
private function loadPlayer():void{
videoPlayer= new Player();
videoPlayer.createPlayer();
}
}
Player.as
public function createPlayer():void{
_loader = new Loader();
_loader.contentLoaderInfo.addEventListener(Event.INIT, onLoaderInit);
_loader.load(new URLRequest(playerType));
}
public function get videoBytesLoaded():Number{
return _Player.getVideoBytesLoaded(); //youtube api method
}
public function get videoBytesTotal():Number{
return _Player.getVideoBytesTotal; //youtube api method
}
private function onLoaderInit(event:Event):void {
_Player=_loader.content;
//only show part of codes....
}
I appreciate any help....Thanks!!!!!
_Player is only defined after the Event.INIT has fired so any call before the _Player value is defined will throw an error.
You should, at the minimum , have this:
public function videoMain (){
loadPlayer()
}
private function onLoaderInit(event:Event):void {
_Player=_loader.content;
//only show part of codes....
loadProgress();
}
but progress events are not static so really you should have an enterFrame event listener in order to listen to the changing values...
private function onLoaderInit(event:Event):void {
_Player=_loader.content;
//only show part of codes....
addEventListener(Event.ENTER_FRAME , enterFrameListener);
}
private function enterFrameListener(event:Event):void
{
loadProgress();
// and here you add some way to remove this event listener when
// the video is fully loaded
}

Resources