I just started with learning how to test within Laravel. I came across some problems though..
I'm testing my controller and want to check if a View has a variable assigned.
My controller code:
class PagesController extends \BaseController {
protected $post;
public function __construct(Post $post) {
$this->post = $post;
}
public function index() {
$posts = $this->post->all();
return View::make('hello', ['posts' => $posts]);
}
}
And my view contains a foreach loop to display all posts:
#foreach ($posts as $post)
{{post->id}}
#endforeach
Last but not least my test file:
class PostControllerTest extends TestCase {
public function __construct()
{
// We have no interest in testing Eloquent
$this->mock = Mockery::mock('Eloquent', 'Post');
}
public function tearDown()
{
Mockery::close();
}
public function testIndex() {
$this->mock->shouldReceive('all')->once()->andReturn('foo');
$this->app->instance('Post', $this->mock);
$this->call('GET', '/');
$this->assertViewHas('posts');
}
}
Now comes the problem, when I run "phpunit" the following error appears:
ErrorException: Invalid argument supplied for foreach()
Any ideas why phpunit returns this error?
Your problem is here:
$this->mock->shouldReceive('all')->once()->andReturn('foo');
$this->post->all() (which is what you're mocking) should return an array, and that's what your view expects. You're returning a string.
$this->mock->shouldReceive('all')->once()->andReturn(array('foo'));
should take care of the error you have, though you'll then get an error of the "Getting property of non-object" type.
You could do this:
$mockPost = new stdClass();
$mockPost->id = 1;
$this->mock->shouldReceive('all')->once()->andReturn(array($mockpost));
You should mock the view as well:
public function testIndex() {
$this->mock->shouldReceive('all')->once()->andReturn('foo');
$this->app->instance('Post', $this->mock);
View::shouldReceive('make')->with('hello', array('posts', 'foo'))->once();
$this->call('GET', '/');
}
Related
I am using codeception,when I try to check dbconnection it shows the error:
> DEPRECATION: Calling the
> "Symfony\Component\EventDispatcher\EventDispatcherInterface::dispatch()"
> method with the event name as first argument is deprecated since
> Symfony 4.3, pass it second and provide the event object first
> instead.
> C:\xampp\htdocs\affiliate_codeception\codeception\vendor\symfony\event-dispatcher\EventDispatcher.php:58"
How do I fix this?
<?php
class adminTest extends \Codeception\Test\Unit
{
/**
* #var \UnitTester
*/
protected $tester;
protected function _before()
{
}
protected function _after()
{
}
// tests
public function testSomeFeature()
{
}
public function tryToTest(UnitTester $I)
{
$I->amConnectedToDatabase('testdb');
//$I->seeInDatabase('users', ['name' => 'Davert', 'email' => 'davert#mail.com']);
}
}
Change the code in vendor/codeception/phpunit-wrapper/src/Listener.php:
Search for
dispatcher->dispatch(
For each of them, swap the first and second arguments.
For example, the first occurrence is:
public function startTestSuite(\PHPUnit\Framework\TestSuite $suite)
{
$this->dispatcher->dispatch('suite.start', new SuiteEvent($suite));
}
Change it to:
public function startTestSuite(\PHPUnit\Framework\TestSuite $suite)
{
$this->dispatcher->dispatch(new SuiteEvent($suite), 'suite.start');
}
I am using version v1.4.2.18. The library can be found here: https://github.com/Youshido/GraphQL
I am trying to accomplish the following:
query {
articleSummary(id:1) {
title,
body,
article {
id
}
}
}
I have an ArticleSummaryField.php:
class ArticleSummaryField extends AbstractField
{
public function build(FieldConfig $config)
{
$config->addArgument('id', new NonNullType(new StringType()));
}
public function getType()
{
return new ArticleSummaryType();
}
public function resolve($value, array $args, ResolveInfo $info)
{
return [
'title' => 'test title',
'body' => 'test body',
'article' => $args['id']
];
}
}
Then the ArticleSummaryType.php:
class ArticleSummaryType extends AbstractObjectType
{
public function build($config)
{
$config
->addField('title', new StringType());
->addField('body', new StringType());
->addField('article', new ArticleField());
}
}
Then the ArticleField.php has the getType method return the ArticleType which has the id field.
However what i am getting is an error:
Fatal error: Uncaught Error: Call to undefined method ArticleField::getNullableType() in .../vendor/youshido/graphql/src/Execution/Processor.php on line 135
What seems to be happening is that when $targetField->getType() on line 135 in src/Execution/Processor.php is called its returning the ArticleField class, not the ArticleType class.
I would expect that to return the class as declared in the 'getType' method on the ArticleField class.
Am i going about this wrong for nesting fields? Or is there a bug in the library?
To accomplish this you only pass the Field class as the first argument.
class ArticleSummaryType extends AbstractObjectType
{
public function build($config)
{
$config
->addField('title', new StringType());
->addField('body', new StringType());
->addField(new ArticleField());
}
}
Then in the field class you can override getName to set the name for the field as needed or it will use the class name as the field name.
How to load a template file from my admin controller in custom module in prestashop 1.6
if (!defined('_PS_VERSION_')) exit;
class QueryAllTrxController extends ModuleAdminController
{
public $module;
public function __construct()
{
parent::__construct();
}
public function initContent()
{
parent::initContent();
$this->setTemplate('display.tpl');
//$this->setTemplate(_PS_THEME_DIR_.'mypage.tpl');
}
}
I had the same problem and it took forever to figure out.
I ended up spotting the solution in this video : https://www.youtube.com/watch?v=CdnJpLqqvcM
Any this is how I got it to work :
1 - Create the controller in ModuleName/controllers/AdminMyControllerNameController.php
class AdminMyControllerNameController extends ModuleAdminController
{
public function __construct()
{
$this->display = 'view';
$this->meta_title = $this->l('metatitle');
$this->toolbar_title = $this->l('tollbartitle');
parent::__construct();
}
public function initContent()
{
$this->show_toolbar = true;
$this->display = 'view';
$this->meta_title = $this->l('META TITLE');
parent::initContent();
$this->setTemplate('templatename.tpl');
}
public function initToolBarTitle()
{
$this->toolbar_title = $this->l('TOOLBAR TITLE??');
}
public function initToolBar()
{
return true;
}
}
2 - Create the template file in ModuleName/views/admin/my_controller_name/template.tpl
You have to create a directory in the views/admin folder using the name of your controller written in snake case.
Anyway I hope this will help.
WORKING CODE HERE
Background:
You want to add a custom admin page with a custom module controller. But you cannot customize template because you're stuck with this error message:
Fatal error: Uncaught --> Smarty: Unable to load template file '/var/www/html/admin-dev/themes/default/template/catalog/index.tpl' <-- thrown in /var/www/html/tools/smarty/sysplugins/smarty_internal_templatebase.php on line 129
Your current source code is:
class AdminYourModuleNameProductsController extends ModuleAdminController {
public function initContent() {
parent::initContent();
// enable these lines if you're stuck with damn stupid blank page with just 500 error
// ini_set('display_errors', '1');
// ini_set('display_startup_errors', '1');
// error_reporting(E_ALL);
$this->setTemplate('products/index.tpl');
}
}
And you don't know what to do because PrestaShop dev doc is the worst document in the history of ecommerce platform developer document and moreover its forum is full of chitchats and junks.
Solution
Place index.tpl at
{%PRESTA_ROOT%}/modules/{%YOUR MODULE DIR%}/views/templates/admin/{% snake case version of controller %}/products/index.tpl
For example, if your module name is yourmodulename and the controller name is AdminYourModuleNameProductsController (as in the example), the correct path is:
{%PRESTA_ROOT%}/modules/yourmodulename/views/templates/admin/your_module_name_products/products/index.tpl
If the error still persists:
Check this line:
{%PRESTA_ROOT%}/classes/controller/ModuleAdminController.php
public function createTemplate($tpl_name)
{
if (file_exists(_PS_THEME_DIR_.'modules/'.$this->module->name.'/views/templates/admin/'.$tpl_name) && $this->viewAccess()) {
// echo the following line and exit
return $this->context->smarty->createTemplate(_PS_THEME_DIR_.'modules/'.$this->module->name.'/views/templates/admin/'.$tpl_name, $this->context->smarty);
} elseif (file_exists($this->getTemplatePath().$this->override_folder.$tpl_name) && $this->viewAccess()) {
// echo the following line and exit
return $this->context->smarty->createTemplate($this->getTemplatePath().$this->override_folder.$tpl_name, $this->context->smarty);
}
// the error occurs because php get reach to the following line:
return parent::createTemplate($tpl_name);
}
Do as I commented and you can get the correct file path. Make sure the file exists in the path.
My PrestaShop version is 1.6.1.24
The code $this->setTemplate('display.tpl'); is loading a template file modules/your-custom-module/views/templates/admin/display.tpl or modules/your-custom-module/display.tpl.
The classname must be named that way: AdminQueryAllTrxController
you can put display.tpl in :
modules\module_name\views\templates\admin\classe_name(QueryAllTrx)
and use :$this->setTemplate('display.tpl'); in your AdminQueryAllTrxController
First of all add controller to your module:
modules\module_name\controllers\admin\SomeNameController.php
and extend it by ModuleAdminController, you need at least two methods for it to work properly __construct and initContent
put the following code to the later method:
$this->content .= $this->context->smarty->fetch($this->pathToTpl);
$this->context->smarty->assign(array(
'content' => $this->content,
));
You could replace $this->pathToTpl with any path which is pointed to your tpl file, I'm prefer to create the path dynamically. You can see a simple example here:
class SomeNameController extends ModuleAdminController{
var $pathToTpl;
public function __construct()
{
$this->bootstrap = true;
$this->context = Context::getContext();
$this->pathToTpl = _PS_MODULE_DIR_ .
$this->module->name . // put the name of module
'/views/templates/admin' .
'/' .
'templateName.tpl';
parent::__construct();
}
public function initContent()
{
parent::initContent();
$this->content .= $this->context->smarty->fetch($this->pathToTpl);
$this->context->smarty->assign(array(
'content' => $this->content,
));
}
}
finally you need to place templateName.tpl in the path you wanted to be:
modules\module_name\views\templates\admin\templateName.tpl
I have a Nancy module which uses a function which expects as parameters a string (a captured pattern from a route) and a method group. When trying to pass the parameter directly it will not compile as I "cannot use a method group as an argument to a dynamically dispatched operation".
I have created a second route which attempts to cast the dynamic to a string, but this always returns null.
using System;
using Nancy;
public class MyModule : NancyModule
{
public MyModule()
{
//Get["/path/{Name}/action"] = parameters =>
// {
// return MyMethod(parameters.Name, methodToBeCalled); // this does not compile
// };
Get["/path/{Name}/anotherAction"] = parameters =>
{
return MyMethod(parameters.Name as string, anotherMethodToBeCalled);
};
}
public Response MyMethod(string name, Func<int> doSomething)
{
doSomething();
return Response.AsText(string.Format("Hello {0}", name));
}
public int methodToBeCalled()
{
return -1;
}
public int anotherMethodToBeCalled()
{
return 1;
}
}
Tested with the following class in a separate project:
using System;
using Nancy;
using Nancy.Testing;
using NUnit.Framework;
[TestFixture]
public class MyModuleTest
{
Browser browser;
[SetUp]
public void SetUp()
{
browser = new Browser(with =>
{
with.Module<MyModule>();
with.EnableAutoRegistration();
});
}
[Test]
public void Can_Get_View()
{
// When
var result = browser.Get("/path/foobar/anotherAction", with => with.HttpRequest());
// Then
Assert.AreEqual(HttpStatusCode.OK, result.StatusCode);
Assert.AreEqual("Hello foobar", result.Body.AsString()); //fails as parameters.Name is always null when cast to a string
}
}
You can find the whole test over on github
I've had similar issues when using 'as' so I tend to use explicitly cast it:
return MyMethod((string)parameters.Name, anotherMethodToBeCalled);
Also I think there was a bug raised with the casing on parameters, but I think it's better to keep them lowercase:
Get["/path/{name}/anotherAction"]
(string)parameters.name
Your code works for me with upper case and lowercase, using the explicit cast.
I have the following PHP code;
<?php
component_customer_init();
component_customer_go();
function component_customer_init()
{
$customer = Customer::getInstance();
$customer->set(1);
}
function component_customer_go()
{
$customer = Customer::getInstance();
$customer->get();
}
class Customer
{
public $id;
static $class = false;
static function getInstance()
{
if(self::$class == false)
{
self::$class = new Customer;
}
else
{
return self::$class;
}
}
public function set($id)
{
$this->id = $id;
}
public function get()
{
print $this->id;
}
}
?>
I get the following error;
Fatal error: Call to a member function set() on a non-object in /.../classes/customer.php on line 9
Can anyone tell me why I get this error? I know this code might look strange, but it's based on a component system that I'm writing for a CMS. The aim is to be able to replace HTML tags in the template e.g.;
<!-- component:customer-login -->
with;
<?php component_customer_login(); ?>
I also need to call pre-render methods of the "Customer" class to validate forms before output is made etc.
If anyone can think of a better way, please let me know but in the first instance, I'd like to know why I get the "Fatal error" mentioned above.
Well, I think your Customer::getInstance() method is flawed. It should look like this:
...
static function getInstance()
{
if(self::$class == false)
{
self::$class = new Customer;
return self::$class; // ADDED!!
}
else
{
return self::$class;
}
}
....
In the if(self::$class == false) branch you are creating the instance of the class, but you dont return it.
You could also rewrite it as such:
static function getInstance()
{
if(self::$class == false)
{
self::$class = new Customer;
}
return self::$class;
}
To make it a bit shorter.
DRY: Don't Repeat Yourself
static function getInstance()
{
if(self::$class == false)
{
self::$class = new Customer;
}
return self::$class;
}
And for Sinlgetons it is also important to prevent __clone() from being used. Making it private should solve that problem:
private function __clone() {}