Sorry for maybe silly question, but I cannot find answer through googling.
My question is:
I created file TaskCest.php under backend\acceptance, In that file have following declaration
use yii\test\FixtureTrait;
public function fixtures() {
return ['tasks' => TasksFixture::className()];
}
I have that fixture class with data in data directory.
But when I run script I get following error:
[yii\base\ErrorException] ltrim() expects parameter 1 to be string, object given
Error is obvious, but I cant understand, in file yii2\test\FixtureTrait.php:145 I have function which expects name parameter to be string but object passed automatically [I dont call getFixture].
What's problem. Did someone faced the same?
-vvv output
Test tests/acceptance/TaskCest.php:getFixture
[yii\base\ErrorException] ltrim() expects parameter 1 to be string, object given
/home/nginx/www/planning-back/vendor/codeception/codeception/src/Codeception/Lib/Di.php:123
/home/nginx/www/planning-back/vendor/codeception/codeception/src/Codeception/Lib/Di.php:123
/home/nginx/www/planning-back/vendor/codeception/codeception/src/Codeception/Test/Cest.php:136
/home/nginx/www/planning-back/vendor/codeception/codeception/src/Codeception/Test/Cest.php:148
/home/nginx/www/planning-back/vendor/codeception/codeception/src/Codeception/Test/Cest.php:82
/home/nginx/www/planning-back/vendor/codeception/codeception/src/Codeception/Test/Test.php:90
/home/nginx/www/planning-back/vendor/phpunit/phpunit/src/Framework/TestSuite.php:728
/home/nginx/www/planning-back/vendor/codeception/codeception/src/Codeception/PHPUnit/Runner.php:98
/home/nginx/www/planning-back/vendor/codeception/codeception/src/Codeception/SuiteManager.php:154
/home/velaro/.config/composer/vendor/codeception/codeception/src/Codeception/Codecept.php:183
/home/velaro/.config/composer/vendor/codeception/codeception/src/Codeception/Codecept.php:152
/home/velaro/.config/composer/vendor/codeception/codeception/src/Codeception/Command/Run.php:282
/home/velaro/.config/composer/vendor/symfony/console/Command/Command.php:255
/home/velaro/.config/composer/vendor/symfony/console/Application.php:829
/home/velaro/.config/composer/vendor/symfony/console/Application.php:191
/home/velaro/.config/composer/vendor/symfony/console/Application.php:122
/home/velaro/.config/composer/vendor/codeception/codeception/src/Codeception/Application.php:103
/home/velaro/.config/composer/vendor/codeception/codeception/codecept:34
Codeception recognize trait's methods like tests (it search all public methods of Cest-class include trait's methods and run it).
You should extract trait to another class FixtureLoader, and include it into your Cest file.
class FixtureLoader
{
use \yii\test\FixtureTrait;
public $fixtures;
public function fixtures()
{
return $this->fixtures;
}
}
abstract class ApiCest
{
/**
* #var FixtureLoader
*/
protected $fixtureLoader;
public function __construct()
{
$this->fixtureLoader = new FixtureLoader();
}
protected function fixtures()
{
return [];
}
public function _before(\FunctionalTester $I)
{
$this->fixtureLoader->fixtures = $this->fixtures();
$this->fixtureLoader->loadFixtures();
}
public function _after(\FunctionalTester $I)
{
$this->fixtureLoader->unloadFixtures();
}
}
class UserCest extends ApiCest
{
protected function fixtures()
{
return [
'users' => UserFixture::className(),
];
}
public function testSomething(\FunctionalTester $I)
{
$I->sendGET('/user');
$I->seeResponseCodeIs(200);
}
}
Related
For studying purposes I was trying to create a GoF Decorator implementation using as example a possibility to convert a text to compressed text or encrypted text.
<?php
interface DataSource {
public function gerar($texto): string;
public function recuperar($texto) : string;
}
class TextoBase implements DataSource {
public function gerar($texto): string {
return $texto;
}
public function recuperar($texto) : string {
return $texto;
}
}
abstract class Decorator implements DataSource {
private DataSource $decorado;
public function __construct(DataSource $decorado) {
$this->decorado = $decorado;
}
public function gerar($texto): string {
return $this->decorado->gerar($texto);
}
public function recuperar($texto) : string {
return $this->decorado->recuperar($texto);
}
}
class CriptoDecorator extends Decorator {
const KEY = 'vDIa5JdknBqfrKOu8d7UpddnBMCH1vza';
const NONCE = 'Ra5LeH7ntW2rvkz3dmqI5Stx';
public function gerar($texto): string {
return $this->encrypt(parent::gerar($texto));
}
public function recuperar($texto): string {
return $this->decrypt(parent::recuperar($texto));
}
public function encrypt($data) {
return sodium_crypto_secretbox($data, self::NONCE, self::KEY);
}
private function decrypt(string $data): string {
return sodium_crypto_secretbox_open($data, self::NONCE, self::KEY);
}
}
class CompressaoDecorator extends Decorator {
const NIVEL_COMPRESSAO = 6;
public function gerar($texto): string {
return $this->comprimir(parent::gerar($texto));
}
public function recuperar($texto): string {
return $this->descomprimir(parent::recuperar($texto));
}
private function comprimir(string $stringData): string {
return gzcompress($stringData, self::NIVEL_COMPRESSAO);
}
private function descomprimir(string $stringData): string {
return gzuncompress($stringData);
}
}
$texto = "olá mundo !";
$decorado = new CompressaoDecorator(new CriptoDecorator(new TextoBase()));
$texto_decorado = $decorado->gerar($texto);
echo PHP_EOL;
echo $decorado->recuperar($texto_decorado);
For some reason I'm got warning:
Warning: gzuncompress(): data error in C:\wamp64\www\curso\designer_patterns\estrutural\decorator\real_life.php on line 93
So, Is there a way to fix this and allow Both Decorators to be stacked and be used to gerar(generate) and recuperar(retrieve) ?
Thanks in advance
You need to unwind in the same order that you setup. If you compress then encrypt, you need to decrypt and then uncompress.
The fast fix for this specific code is to change your recuperar method in CompressaoDecorator
class CompressaoDecorator extends Decorator
{
public function recuperar($texto): string
{
return parent::recuperar($this->descomprimir($texto));
}
}
If you want to solve this in the abstract, I would handle this with a factory instead that can guarantee order. To do that, I don't think the individual objects themselves should concern themselves with parent, the factory should do the job of chaining things.
Edit
Actually, as I think about this more, you don't need the factory, you just need to swap your order for all of your recuperar methods, so this one would change, too:
class CriptoDecorator extends Decorator
{
public function recuperar($texto): string
{
return parent::recuperar($this->decrypt($texto));
}
}
This should allow you to call either encrypt or compress first, and as long as you use the same chain the reverse should work, too.
I have a class, that has a constructor that looks like this:
use App\Libraries\Content\ContentInterface;
use EllipseSynergie\ApiResponse\Laravel\Response;
class ImportController extends Controller
{
private $indexable;
function __construct(Response $response, ContentInterface $contentInterface) {
$this->indexable = \Config::get('middleton.wp.content.indexable_types');
$this->response = $response;
$this->contentInterface = $contentInterface;
}
public function all() {
$include = array_diff($this->indexable, ['folder']);
$importResult = $this->import($include);
$this->deleteOldContent($importResult['publishedPostsIDs']);
return $importResult['imported'];
}
How can I instantiate this class from another class and call the method all() from it?
I have tried with something like this:
use EllipseSynergie\ApiResponse\Laravel\Response;
use App\Libraries\Content\ContentInterface;
class ContentImport extend Command {
public function handle() {
(new ImportController(new Response, new ContentInterface))->all();
}
But, that doesn't work, I get the error that I should pass the arguments to the Response class too:
[Symfony\Component\Debug\Exception\FatalThrowableError]
Type error: Too few arguments to function EllipseSynergie\ApiResponse\AbstractResponse::__construct(), 0 passed in /home/
vagrant/Projects/middleton/app/Console/Commands/ContentImport.php on line 43 and exactly 1 expected
What is the correct way of doing this?
I believe this should work
use EllipseSynergie\ApiResponse\Laravel\Response;
use App\Libraries\Content\ContentInterface;
class ContentImport extend Command {
public function handle(ImportController $importController) {
$importController->all();
}
I have following problem. I doing API for our web and customer must use his function as callback in my function.
Example:
UserClass {
userMethod() {
return $data;
}
}
MyClass {
myFunction (callback ) {
doingSomething();
doingSomething();
$data = call_user_function($callback);
return doingSomethingWithData($data);
}
}
Problem is, that this is API and I cant implement customer class as callback, because non exist, but i need test that function will be work with expected data. Is there any possibilites how test my function using phpunit?
Thanks a lot
Just pass in anonymous function that returns expected result the one you can predict output for. Make sure that it handles garbage data out/edge cases properly.
Your test can look something like that:
class MyClassTest extends PHPUnit_Framework_TestCase
{
/**
* #dataProvider myFunctionProvider
*/
public function testMyFunction($callback, $expected)
{
$this->assertEquals(
// Just as example you can create instance of class and call it.
MyClass::MyFunction($callback),
$expected
);
}
public function myFunctionProvider()
{
return [
[ function () { return 'a';}, 'a'],
[ function () { return 'c';}, 'c'],
[ function () { return 'b';}, 'b']
];
}
}
As a side note change your code to:
function MyFunc(callable $callback) {
}
That'll make sure you get only callable in to your function.
I'm attempting to build a role based access control in our PHP framework. The framework is on MVC architecture so every path works on /controller/action/param. We can get the controller and action on initialization and store them in variables, $controller, $action. Now my idea is to use a class to check the permissions of this action like:
Auth::permissions($controller, $action);
Now I'm hoping I could somehow create a script which would find all public methods of controllers inside a /modules/ folder. This way I could just run a script and it would update all controller actions as a list to the database, where we would get the role permissions from. This way I could avoid inserting all controller actions manually. Getting all the controllers is very easy as the folder structure is as:
/modules
/controller
controller.php
So I can just find all subdirectories on modules and add .php in the end. My question is that can I get the file's public methods somehow?
class Example extends Controller {
public function main() {
return 'foo';
}
}
This way I could store this in the database as
example | main | role_id
Here is a little code that can help you:
<?php
class Example {
public function main() {
return 'foo';
}
private function privatefunc(){
}
public function anotherpublicfunc(){
}
}
$reflector = new ReflectionClass("Example");
foreach($reflector->getMethods() as $method){
if($method->isPublic()) {
echo "Method ".$method->name." is public".PHP_EOL;
}else{
echo "Method ".$method->name." is not public".PHP_EOL;
}
}
?>
output:
Method main is public
Method privatefunc is not public
Method anotherpublicfunc is public
If you want to get public methods of a class then you can use get_class_methods read the doc here
class Car {
public function permission_method_two() {
}
public function permission_method_three() {
}
private function private_function() {
}
}
echo '<pre>'.print_r(get_class_methods('Car'),1).'</pre>';
// prints only public methods:
Array
(
[0] => permission_method_two
[1] => permission_method_three
)
You can follow convention:
- each public methods start without lowdash
- each private and protected method start with lowdash
Example
class Example
{
public function publicMethod()
{
}
private function _privateMethod()
{
}
protected function _protectedMethod()
{
}
}
and then use http://php.net/manual/ru/function.get-class-methods.php
foreach(get_class_methods('Example') as $methodName){
if(strpos($methodName, '_') !== 0) $publicMethod[] = $methodName;
}
I'm still fresh in phpspec but usually I'm finding a solution when I struggle with something but this one is tough.
I've tried many different approaches and I haven't found a solution. I'm using Symfony2.
I have a class that I want to test:
class MyClass
{
public function getDataForChildren(MyObject $object)
{
foreach ($object->getChildren() as $child) {
$query = \json_decode($child->getJsonQuery(), true);
$data = $this->someFetcher->getData($query);
$child->setData($data);
}
return $object;
}
}
And here's how look my spec class:
class MyClassSpec
{
function let(SomeFetcher $someFetcher)
{
$this->beConstructedWith($someFetcher);
}
function it_is_initializable()
{
$this->shouldHaveType('MyClass');
}
function it_should_get_data_for_children_and_return_object(
MyClass $object,
MyClass $child, // it means that MyClass has a self-reference to MyClass
$someFetcher
)
{
$query = '{"id":1}';
$returnCollection = new ArrayCollection(array($child));
$object->getChildren()->shouldBeCalled()->willReturn($returnCollection);
$child->getJsonQuery()->shouldBeCalled()->willReturn($query);
$someFetcher->getData($query)->shouldBeCalled();
$this->getDataForChildren($object);
}
}
And after running phpspec I'm getting this error:
warning: json_decode() expects parameter 1 to be string, object given in
I have no idea how to solve this problem. If anyone has a clue, please help.
This is a common stumbling block with PhpSpec, the declaration:
MyClass $child
means that a Collaborator object of $child will be set up with the same interface of MyClass.
When child->getJsonQuery() is called in the SUT (class you're testing), it will return a MethodProphecy not the string you expect it to return.
What you want to say is that your ArrayCollection will contain not $child itself (which is a Collaborator object), but the real object that the collaborator is wrapped around. You do it like this:
$returnCollection = new ArrayCollection(array($child->getWrappedObject()));
In addition, you should not be using (i.e. is is superfluous) both
shouldBeCalled() and willReturn() on the same Collaborator, one or the
other is sufficient. If you've specified what the collabrator will
return, it is clear that it is going to be called witin the SUT.
shouldBeCalled() should be used in the "assert" part of the test in
order to confirm that the Collaborator was called with the expected
arguments, or at the right time.
Your final SUT and spec should look something like this:
class MyClass
{
/**
* #var SomeFetcher
*/
private $someFetcher;
public function getDataForChildren(MyObject $object)
{
foreach ($object->getChildren() as $child) {
$query = \json_decode($child->getJsonQuery(), true);
$data = $this->someFetcher->getData($query);
$child->setData($data);
}
return $object;
}
public function getJsonQuery()
{
}
public function setData()
{
}
public function __construct(SomeFetcher $someFetcher)
{
$this->someFetcher = $someFetcher;
}
}
class MyClassSpec extends ObjectBehavior
{
function let(SomeFetcher $someFetcher)
{
$this->beConstructedWith($someFetcher);
}
function it_should_get_data_for_children_and_return_object(
MyObject $object,
MyClass $child, // it means that MyClass has a self-reference to MyClass
SomeFetcher $someFetcher
)
{
$query = '{"id":1}';
$returnCollection = new ArrayCollection(array($child->getWrappedObject()));
$object->getChildren()->willReturn($returnCollection);
$child->getJsonQuery()->willReturn($query);
$child->setData(Argument::any())->shouldBeCalled();
$someFetcher->getData(array('id' => 1))->shouldBeCalled();
$this->getDataForChildren($object);
}
}
Also, the line
$query = \json_decode($child->getJsonQuery(), true);
Will produce a associated array in $query, i.e. array('id' => 1) (this is what the second 'true' argument to json_encode stipulates), therefore you'd expect $someFetcher->getData() to be called with the latter, hence:
$someFetcher->getData(array('id' => 1))->shouldBeCalled();