I am creating a new project from scratch on a Mac Catalina 10.15 with php v7.3.27 and composer v2.0.12. I have created a Codeception project before but on a different computer. I followed the quickstart guide using acceptance tests.
I generated the page objects using "php vendor/bin/codecept generate:pageobject acceptance " and generated the Cests using "php vendor/bin/codecept generate:cest acceptance "
When I try to run any tests, it says that the class isn't found. If I hover over my pages or over the word AcceptanceTester, it does not link me to anything. My project looks like this:
tests
> data
> output
v support
v _generated
AcceptanceTesterActions.php
FunctionalTesterActions.php
UnitTesterActions.php
> Helper
> Page/Acceptance
CustomerSignupHomePage.php
AcceptanceTester.php
FunctionalTester.php
UnitTester.php
v acceptance
CustomerSignupCest.php
I realized I didn't have a .bashrc in my home dir, so I created one and I updated the bash_profile similar to what I had on my working computer:
#Global composer bin
export $PATH=~/.composer/vendor/bin:$PATH
export $PATH="$HOME/.composer/vendor/bin:$PATH"
cat .bash_profile
#alias composer="php /usr/local/bin/composer.phar"
export PATH="./composer/vendor/bin/:$PATH"
export PATH="/usr/local/opt/php#7.3/bin:$PATH"
export PATH="/usr/local/opt/php#7.3/sbin:$PATH"
~
My codeception.yml looks like this:
tests: tests
output: tests/_output
data: tests/_data
support: tests/_support
envs: tests/_envs
actor_suffix: Tester
extensions:
enabled:
- Codeception\Extension\RunFailed
My composer.json looks like this:
"require-dev": {
"codeception/codeception": "^4.1",
"codeception/module-phpbrowser": "^1.0.0",
"codeception/module-asserts": "^1.0.0"
},
"require":{
"ext-zip": "*",
"guzzlehttp/guzzle": "^7.2",
"vlucas/phpdotenv": "^5.2"
},
"autoload": {
"psr-4": {
"Tests\\Support\\": "tests/_support/"
}
}
}
My one Cest file looks like this:
use Tests\Support\Page\Acceptance\CustomerSignupHomePage;
class CustomerSignupCest
{
const ADDRESS = "100 Silmarillion Trail, Austin TX 78739";
const NAME = "POC Automation Tester";
const PHONE = "512-203-5555";
/**
* signUp
*
* #param AcceptanceTester $I
* #return void
*/
public function signUp(AcceptanceTester $I)
{
$I->wantToTest("the signup flow");
CustomerSignupHomePage::fillGetQuickAndEasyPrice($I, self::ADDRESS, self::NAME, self::PHONE);
}
}
My page class looks like this:
namespace Tests\Support\Page\Acceptance;
class CustomerSignupHomePage
{
// include url of current page
public static $URL = 'https://dev-www.<mycompany>.com';
/**
* Declare UI map for this page here. CSS or XPath allowed.
* public static $usernameField = '#username';
* public static $formSubmitButton = "#mainForm input[type=submit]";
*/
const ADDRESS_INPUT = "#streetAddress";
const SEE_PRICE_BUTTON = ".js-submitButton";
const NAME_INPUT = ".js-fullNameInput";
const PHONE_INPUT = ".js-phoneInput";
/**
* fillGetQuickAndEasyPrice
*
* #param AcceptanceTester $I
* #param mixed $address
* #param mixed $name
* #param mixed $phone
* #return void
*/
public static function fillGetQuickAndEasyPrice(AcceptanceTester $I, $address, $name, $phone)
{
$I->waitForElement(self::ADDRESS_INPUT);
$I->fillField(self::ADDRESS_INPUT, $address);
$I->click(self::SEE_PRICE_BUTTON);
$I->fillField(self::NAME_INPUT, $name);
$I->fillField(self::PHONE_INPUT, $phone);
$I->click(self::SEE_PRICE_BUTTON);
}
/**
* Basic route example for your current URL
* You can append any additional parameter to URL
* and use it in tests like: Page\Edit::route('/123-post');
*/
public static function route($param)
{
return static::$URL.$param;
}
/**
* #var \AcceptanceTester;
*/
protected $acceptanceTester;
public function __construct(\AcceptanceTester $I)
{
$this->acceptanceTester = $I;
}
}
If I try to run the tests, I get this error:
[TypeError] Argument 1 passed to Tests\Support\Page\Acceptance\CustomerSignupHomePage::fillGetQuickAndEasyPrice() must be an instance of Tests\Support\Page\Acceptance\AcceptanceTester, instance of AcceptanceTester given, called in /Users/julie/workspaces/Codeception-Demo/tests/acceptance/CustomerSignupCest.php on line 21
My AcceptanceTester.php is the one automatically generated by codeception:
/**
* Inherited Methods
* #method void wantToTest($text)
* #method void wantTo($text)
* #method void execute($callable)
* #method void expectTo($prediction)
* #method void expect($prediction)
* #method void amGoingTo($argumentation)
* #method void am($role)
* #method void lookForwardTo($achieveValue)
* #method void comment($description)
* #method void pause()
*
* #SuppressWarnings(PHPMD)
*/
class AcceptanceTester extends \Codeception\Actor
{
use _generated\AcceptanceTesterActions;
/**
* Define custom actions here
*/
}
My Cest file and Page file doesn't see AutomationTester at all. If I start typing $I-> it does not see any codeception methods. I have added the Codeception marketplace extension to Vscode. If I hover over AcceptanceTester in the param of my method, it doesn't take me to the class file. If I hover over the page, it doesn't take me to the page object file. It is like I am flying blind. The error happens in vscode and outside vscode in terminal.
I have tried:
Adding "use AcceptanceTester" either above or below classname and with relative path/absolute path, does not work
Restarting vscode
Running build commands
Trying to extend AcceptanceTester (doesn't work)
Googling and looking at other SO answers
Removing namespace from the page (makes it worse, says page not found then)
Commenting out the call to the page object and using var_dump("hello world")- makes the tests pass but obviously then tests do nothing
realized i also had zshell and but also no .zshrc so tried the same commands as the bash ones. Also sourced the bash profile
ran the autodump command
Tried to follow the error message exactly and moved the AcceptanceTester file in the /Page/Acceptance folder, but then when I run tests, the error changes to " AcceptanceTester class doesn't exist in suite folder. Run the 'build' command to generate it" If I run build, then it puts the AcceptanceTester.php right back where it was!
Blew out my whole project and started over with no page files, only the single class file, following the quickstart docs with the bootstrap instructions. The Cest still doesn't see AcceptanceTester $I as related to the AcceptanceTester.php and says "Call to undefined method AcceptanceTester::waitForElement". Retried steps from above with no luck.
Pushed everything up to git, then closed on the working computer and got a totally different error: The "/Users/julielaursen/git/Codeception-Demo/tests/functional/" directory does not exist. I have not set up the project for functional but for acceptance.
You need to specify backslash before AcceptanceTester class name in the methods signatures to find class in the root namespace:
public function signUp(\AcceptanceTester $I)
public static function fillGetQuickAndEasyPrice(\AcceptanceTester $I, $address, $name, $phone)
Related
This question assumes some knowledge of Laravel, Behat, and Mink.
With that in mind, I am having trouble making a simple call to the DB from within my Behat FeatureContext file which looks somewhat like this...
<?php
use App\Models\Db\User;
use Behat\Behat\Context\Context;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\TableNode;
use Behat\MinkExtension\Context\MinkContext;
/**
* Defines application features from the specific context.
*/
class FeatureContext extends MinkContext implements Context {
public function __construct() {}
/**
* #Given I am authenticated with :email and :password
*/
public function iAmAuthenticatedWith($email, $password) {
User::where('email', $email)->firstOrFail();
$this->visitPath('/login');
$this->fillField('email', $email);
$this->fillField('password', $password);
$this->pressButton('Login');
}
}
When this scenario runs I get this error...
Fatal error: Call to a member function connection() on null (Behat\Testwork\Call\Exception\FatalThrowableError)
Which is caused by this line...
User::where('email', $email)->firstOrFail();
How do I use Laravel Eloquent (make DB calls) from within a Behat/Mink FeatureContext? Do I need to expose something within the constructor of my FeatureContext? Update/add a line within composer.json or behat.yml file?
If there is more than one way to solve this problem and it is worth mentioning, please do.
Additional Details
Laravel: 5.5.*
Behat: ^3.3
Mink Extension: ^2.2
Mink Selenium 2 Driver: ^1.3
Behat Config
default:
extensions:
Behat\MinkExtension\ServiceContainer\MinkExtension:
base_url: "" #omitted
default_session: selenium2
selenium2:
browser: chrome
Laravel need to setup the eloquent and the connection for this to work.
The easy way is to extend laravel TestCase and in the __constructor() call parent::setUp();
It will setup your test environment, like it does when you run php test units in Laravel:
/**
* Setup the test environment.
*
* #return void
*/
protected function setUp()
{
if (! $this->app) {
$this->refreshApplication();
}
$this->setUpTraits();
foreach ($this->afterApplicationCreatedCallbacks as $callback) {
call_user_func($callback);
}
Facade::clearResolvedInstances();
Model::setEventDispatcher($this->app['events']);
$this->setUpHasRun = true;
}
The refreshApplication() will call the createApplication() and it does bootstrap the Laravel and create the $this->app object.
/**
* Refresh the application instance.
*
* #return void
*/
protected function refreshApplication()
{
$this->app = $this->createApplication();
}
I'd like to be able to use PhpStorm's "Go To Declaration" feature (Command + B on a Mac) in Gherkin feature files when using Codeception. However, PhpStorm doesn't seem to figure out where the steps are defined, and outputs this warning:
Undefined step reference: […]
When I'm using Behat, PhpStorm understands where the steps are defined.
Steps to reproduce
mkdir codeception
cd codeception
composer require "codeception/codeception" --dev
./vendor/bin/codecept bootstrap
./vendor/bin/codecept generate:feature acceptance first
Open the project directory in PhpStorm.
Make sure that PhpStorm knows that Codeception is installed:
Make sure that the PhpStorm plugins Gherkin and Codeception Framework are installed.
Add a step to tests/acceptance/first.feature.
./vendor/bin/codecept gherkin:snippets acceptance
This results in the following code. (Not everything is included – let me know if I need to add anything.)
tests/acceptance/first.feature:
Feature: first
In order to ...
As a ...
I need to ...
Scenario: try first
When I visit "/"
tests/_support/AcceptanceTester.php:
<?php
/**
* Inherited Methods
* #method void wantToTest($text)
* #method void wantTo($text)
* #method void execute($callable)
* #method void expectTo($prediction)
* #method void expect($prediction)
* #method void amGoingTo($argumentation)
* #method void am($role)
* #method void lookForwardTo($achieveValue)
* #method void comment($description)
* #method \Codeception\Lib\Friend haveFriend($name, $actorClass = NULL)
*
* #SuppressWarnings(PHPMD)
*/
class AcceptanceTester extends \Codeception\Actor
{
use _generated\AcceptanceTesterActions;
/**
* Define custom actions here
*/
/**
* #When I visit :arg1
*/
public function iVisit($arg1)
{
throw new \Codeception\Exception\Incomplete("Step `I visit :arg1` is not defined");
}
}
However, PhpStorm doesn't know where iVisit() is. How can I fix this?
Currently PhpStorm seems to use the Behat Context interface to determine which classes define implementations for the Gherkin steps in .feature files, so a workaround to have PhpStorm find the steps in the codeception tester is to add the Behat\Behat\Context\Context interface somewhere in your source tree
/* Context.php */
namespace Behat\Behat\Context;
interface Context { }
and then have the AcceptanceTester implement that interface (which is an empty marker interface)
class AcceptanceTester extends \Codeception\Actor implements Context ...
Not supported yet, please vote:
https://youtrack.jetbrains.com/issue/WI-34963
Building off Roverwolf's answer.
Add this to the top of the AcceptanceTester file.
namespace Behat\Behat\Context {
interface Context { }
}
Then have AcceptanceTester implement that. Wrapping namespaces like this is a common trick in PHP testing to fake methods that exist in other namespaces.
namespace {
class AcceptanceTester extends \Codeception\Actor implements \Behat\Behat\Context\Context
}
}
I have another step definition file, and I use Behat's Context. Then implements Context in the class declaration.
use Behat\Behat\Context;
class myClassSteps implements Context\Context
{
step definitions
}
It works for me.
What's the best way ( or maybe the way it's actually done ) of creating an artisan command for generating custom classes or files? Like php artisan make:console itself that creates a php class for our new artisan command.
From what I can think of, we have two options:
Add the template for that new file using php heredoc(or any string inside the new command's class file for that matter), which is really messy.
Put a template file somewhere, read it, replace what's necessary, and then create the new file. But I don't know where would be best to put the template file.
So is there a best-practice for handling this situation in Laravel? I googled it, but there was only articles and documentation for simple artisan command creation.
Update 04/2020: Laravel 7 comes with a way to edit the default stubs to make changes to them and have Laravel pick up those changes. If you want to make a completely different stub to publish a totally different file the process below is appropriate otherwise look at the docs at the link below.
https://laravel.com/docs/7.x/artisan#stub-customization
I know this question is a bit old but this is pretty easy if you just want to create a similar file that Laravel already does. (I wanted to create a job with some custom traits attached on creation)
So first look at the stubs Laravel comes with here on github.
Next, pick the stub of the type of class you want (I copied the job-queued stub) and paste it somewhere you can access in your app. I put mine inside App\Console\Stubs since that makes sense that commands will use the stubs.
After that, create your artisan command with php artisan make:command commandName.
Inside the command created use this file Illuminate\Console\GeneratorCommand. Now make your command extend this class instead of Command; This class is the class Laravel uses to create classes and it extends Command itself.
Inside your command create a few properties and methods as follows:
protected $name = 'make:custom-file'; The name of your command. This replaces $signature
protected $description = 'Command description.';
protected $type = 'Job'; Type of class to make
//location of your custom stub
protected function getStub()
{
return app_path().'/Console/Stubs/custom-job.stub';
}
//The root location the file should be written to
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace.'\Jobs';
}
//option flags if any see this for how it works
protected function getOptions()
{
return [];
}
A full example of how the class should look is like this:
<?php
namespace App\Console\Commands;
use Illuminate\Console\GeneratorCommand;
class CustomJob extends GeneratorCommand
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $name = 'make:custom';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Create a custom job.';
/**
* The type of class being generated.
*
* #var string
*/
protected $type = 'Job';
/**
* Get the stub file for the generator.
*
* #return string
*/
protected function getStub()
{
return app_path().'/Console/Stubs/custom-job.stub';
}
/**
* Get the default namespace for the class.
*
* #param string $rootNamespace
* #return string
*/
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace.'\Jobs';
}
/**
* Get the console command options.
*
* #return array
*/
protected function getOptions()
{
return [];
}
}
Once you run your custom artisan command it will write your custom stub to where you specify.
Laravel uses .stub files as templates, and replaces the tokens inside the template.
Since you mentioned the make:console command, for reference you can take a look at the following files:
vendor/laravel/framework/src/Illuminate/Foundation/Console/stubs/console.stub
(on github)
This the template for making new console commands.
vendor/laravel/framework/src/Illuminate/Foundation/Console/ConsoleMakeCommand.php
(on github)
This is the code that is executed when you run the php artisan make:console command.
If you want to take a look at packages that have done this, as well, a good example is the generators package by Jeffrey Way at Laracasts.
I am trying to write some Behat tests for an application and I need to separate the Contexts so I can use some core elements in different other contexts for example the logged in user step.
behat.yml
suites:
common:
type: symfony_bundle
bundle: CommonBundle
mink_session: symfony2
mink_javascript_session: selenium2
autoload:
'CommonContext': %paths.base%/src/xxx/CommonBundle/Features/Context
contexts: [ xxx\CommonBundle\Features\Context\CoreContext ]
user:
type: symfony_bundle
bundle: UserBundle
mink_session: symfony2
mink_javascript_session: selenium2
autoload:
'UserContext': %paths.base%/src/xxx/UserBundle/Features/Context
contexts:
- Behat\MinkExtension\Context\MinkContext
- xxx\CommonBundle\Features\Context\CoreContext
- xxx\UserBundle\Features\Context\UserContext
DefaultContext.php
namespace XXX\CommonBundle\Features\Context;
use ...
/**
* Class DefaultContext
*/
class DefaultContext extends MinkContext implements Context, KernelAwareContext
{
/**
* #param AbstractEntity $oEntity
*
* #return object
*/
protected function getRepository(AbstractEntity $oEntity)
{
return $this->getService($oEntity);
}
/**
* #return mixed
*/
protected function getEntityManager()
{
return $this->getService('doctrine')->getManager();
}
/**
* #param $id
*
* #return object
*/
protected function getService($id)
{
return $this->getContainer()->get($id);
}
CoreContext.php
namespace XXX\CommonBundle\Features\Context;
use ...
/**
* Class SubContext
*/
class CoreContext extends DefaultContext implements Context, SnippetAcceptingContext
{
/**
* #Given I visit the homepage
* #When I visit the homepage
*/
public function iVisitHomepage()
{
$this->visitPath('/');
}
/**
* #And /^I am logged in as "([^"]*)"$/
* #Then /^I am logged in as "([^"]*)"$/
*/
public function iAmLoggedInAs($username)
{
$user = $this->getContainer()->get('fos_user.user_manager')->findUserByUsername($username);
$this->visit('/login');
$this->fillField('_username', $user->getEmailCanonical());
$this->fillField('_password', self::USER_PASSWORD);
$this->pressButton('_submit');
}
UserContext.php
namespace xxx\UserBundle\Features\Context;
use ...
/**
* Defines application features from the specific context.
*/
class UserContext extends CoreContext implements Context, SnippetAcceptingContext
{
/**
* Initializes context.
*
* Every scenario gets its own context instance.
* You can also pass arbitrary arguments to the
* context constructor through behat.yml.
*/
public function __construct()
{
}
}
register_user.feature
Feature: Register user
#javascript
Scenario: Register user
Given I am on homepage
And I go to "/register/"
And I am logged in as "foo#bar.com"
Then I should see "Terms and Conditions"
So when I run the test I get an error saying this:
Given I am on homepage
Step "I visit the homepage" is already defined in xxx\CommonBundle\Features\Context\CoreContext::iVisitHomepage()
xxx\CommonBundle\Features\Context\CoreContext::iVisitHomepage()
xxx\CommonBundle\Features\Context\CoreContext::iVisitHomepage()
And I go to "/register/"
Step "I visit the homepage" is already defined in xxx\CommonBundle\Features\Context\CoreContext::iVisitHomepage()
xxx\CommonBundle\Features\Context\CoreContext::iVisitHomepage()
xxx\CommonBundle\Features\Context\CoreContext::iVisitHomepage()
Do i understood this completely wrong or I am missing some settings here?
Do not extend contexts that provide step definitions. There's no way around this.
Good extensions provide contexts with handy methods but no step definitions. In case of the Mink extension, next to the MinkContext you also have RawMinkContext. First provides step definitions and should not be extended. The other one provides helper methods you might be interested in. The RawMinkContext is the one you should extend. Alternatively you can also use the MinkAwareTrait.
There has been a shift on what Contexts where in Behat2 and what they are now in Behat3. Contexts in Behat3 are more about "situations" in which your tests might occur. For instance:
Logged in
Anonymous visitor
Public API call
Authenticated API call
Disabled javascript browser
Enabled javascript browser
And so on.
So I'm afraid that the problem is in your configuration of the CoreContext being used in two suites. You can try to avoid this by running your suites separately so it does not load both configurations (and it does not autoload twice the same Context) or to change the Context strategy to not use the autoload, but something different like encapsulating common step logics into their own classes that are then being used from different Contexts.
Hope it helps
I'm looking for a way to make PHPStorm hide some methods from code completion. I've tried to annotate the DocBlocks with #access private but that does not hide them from view.
Is there any way to hide private API, short of writing/generating a stub file with a limited interface and referencing that in my project?
for example:
Lets say the library has this in it:
<?php
interface IDoABunchOfStuff
{
/**
* My library users use this
*/
public function doFoo();
/**
* My Library needs this but requires that my users don't see it.
*
* #access private
* #visibility none
* #package mylib
* #internal
*/
public function doBar();
}
class Foo extends Something implements IDoABunchOfStuff
{
/**
* does foo
*/
public function doFoo()
{
// ...
}
/**
* does bar. for internal use only
*
* #access private
* #visibility none
* #package mylib
* #internal
*/
public function _doBar()
{
// ...
}
}
And my library user is typing:
<?php
myAwesomeFunction(IDoABunchOfStuff $aFoo)
{
if($->$oFoo->[CTRL+SPACE] // invoking code completion...
Is it possible to (and if it is how do I) make it so that my user never sees _doBar?
Neither of the different annotations i've tried seem to have the desired effect.
P.S. I'm using PHPStorm 4.0.3
additional:
In this case I am implementing ArrayAccess and I don't want offsetGet, offsetSet, offsetExists and offsetUnset cluttering up my code completion window but I've had similar problems elsewhere enough to warrant asking a more generalized question.
Nope -- you cannot do such thing in current version of PhpStorm.
There is a ticket on Issue Tracker that suggests using #access tag for this purpose, but currently it is not scheduled to be implemented for any particular version: http://youtrack.jetbrains.com/issue/WI-5788
Feel free to vote/comment/etc and maybe it will be implemented sooner.