I'm using codeception to run acceptance tests for a laravel app. One problem I've been into is is that my login tests start failing when the login page gets cached and, I guess, I'm getting logged in automatically. I think this is the case because my tests start passing again when I clear the cache and they'll generally start failing without my having changed the tests or the application code at all.
Here's the login test in question, now extracted into a helper method
public function login($username, $password, $I) {
$I->amOnPage('users/login');
$I->fillField('email', $username);
$I->fillField('password', $password);
$I->click('Login');
$I->amOnPage('admin/');
$I->see('Welcome');
}
I've been clearing the cache periodically whenever the tests fail but it's becoming tedious. I'd like to be able to register a helper to clear the cache for me and just call the function in all my tests. I extracted the function into a helper as suggested here with the following function in AcceptanceHelper.php:
public function clearCache($I) {
$cache = $this->getModule('Cache');
$cache->flush();
}
This seems to be what was suggested by the documentation here, but I get the error "Module Cache couldn't be connected". It looked like I needed the Laravel4 module so I added that to my acceptance.suite.yml file but no luck there either I received this error:
SQLSTATE[28000] [1045] Access denied for user 'stephen'#'localhost' (using password: NO)
I thought I'd need to authorize mysql in the config file, but that didn't seem to work either. Here's my acceptance.suite.yml file:
class_name: AcceptanceTester
modules:
enabled:
- PhpBrowser
- AcceptanceHelper
- Laravel4
config:
PhpBrowser:
url: 'http://104.131.29.69:8000/'
Laravel4:
user: 'root'
password: 'pAsSwOrD'
Finally I read this answer and it seemed as though I shouldn't actually have included Laravel4 in the config file and that my helper function should look more like this:
public function clearCache($I) {
$L = $I->getLaravel4();
Cache::flush();
}
But I just wind up getting this error instead:
Class 'Codeception\Module\Cache' not found
So I'm stuck. Thanks!
Artisan::call('cache:clear');
is better approach.
Ok so I figured out how to do this. Apparently there's a module called cli which you can include in the acceptance.suite.yml file like this:
class_name: AcceptanceTester
modules:
enabled:
- PhpBrowser
- AcceptanceHelper
- Cli
This module allows you to use shell commands in your script with the runShellCommand() function. Since my cache is stored in files in the app/storage/cache/ directory the shell command I need to execute is:
rm app/storage/cache/*
and voilá cache cleared. Then in the test file it will look like this:
$I->runShellCommand('rm -rf app/storage/cache/*');
I decided to simplify this a bit by including it in the function I used to login, since I knew I'd want to clear the cache before each login I just included that line in a login function inside AcceptanceHelper, which then is accessible in all of my tests.
This is a bit of workaround since it isn't agnostic to the kind of caching I'm using (if I were to use memcached I would need to do something different) but it worked for me so I thought I'd share it.
Related
I followed Codeception's quick start guide (http://codeception.com/quickstart) and read their documentation (http://codeception.com/docs/05-UnitTests).
I have managed to set up the testing environment, and
Created the unit test file (php codecept.phar generate:test unit ExampleTest)
Run the test command (php codecept.phar run unit ExampleTest), which returns an error:
There was 1 error:
1) ExampleTest: Validation
Test tests\unit\ExampleTest.php:testValidation
[Error] Class 'User' not found
#1 ExampleTest->testValidation
#2 C:\laragon\www\kario\vendor\bin\codecept.phar:5
How does the test file know which PHP file to run the test on?
My laragon project is named kario, and sits in C:\laragon\www\kario\resources\views\pages\orders while the test unit file is in C:\laragon\www\kario\vendor\bin\tests\unit.
I had this question too and found the answer for myself. Posted here http://phptest.club/t/beginner-codeception-unit-test-help/1849 but also, here you go:
First, some details. I am using Codeception v2.4.1, powered by PHPUnit 7.1.4. The answer is:
In codeception.yml, add these two lines:
settings:
bootstrap: _bootstrap.php
Here _bootstrap.php can be whatever you want the name of your bootstrap file to be.
You must place _bootstrap.php in each of the following directories as follows:
tests/unit/_bootstrap.php
tests/functional/_bootstrap.pp
tests/acceptance/_bootstrap.php
In my tests/unit/_bootstrap.php file, I placed the following code:
<?php
use Codeception\Util\Autoload;
Autoload::addNamespace('myclassnamespace', __DIR__ . '/../../Classes/');
To make sure I had the right path to Classes, I used trigger_error(__DIR__) in my _bootstrap.php before I added the Autoload line.
Then in my tests/unit/TestAddCest.php, I placed the following line at the beginning of the file:
<?php
use mynamespace;
And in my test function looks like this (note the instantiation of the User class):
public function tryToTest(UnitTester $I)
{
$user = new mynamespace\User('someusername');
$I->assertEquals('someusername', $user->username);
}
I hand typed that function because I'm not on the same machine with the code and didn't feel like getting it over, so it may have a typo or bug, but you get the idea.
Edit 05/01/2018: someone else answered me on http://phptest.club:
It would be better to configure autoloading of your classes in composer.json and let Composer to do the rest, unless you try to avoid using Composer.
https://getcomposer.org/doc/01-basic-usage.md#autoloading
recently upgraded a 5.3 project to 5.4 and all seemed good.
Today I started to implement Dusk however had hit an issue when running the example test
☁ footy-finance [5.4] ⚡ php artisan dusk
PHPUnit 6.0.0 by Sebastian Bergmann and contributors.
E 1 / 1 (100%)
Time: 162 ms, Memory: 6.00MB
There was 1 error:
1) Tests\Browser\ExampleTest::testBasicExample
ReflectionException: Class config does not exist
/Users/owen/Sites/footy-finance/vendor/laravel/framework/src/Illuminate/Container/Container.php:681
/Users/owen/Sites/footy-finance/vendor/laravel/framework/src/Illuminate/Container/Container.php:565
/Users/owen/Sites/footy-finance/vendor/laravel/framework/src/Illuminate/Foundation/helpers.php:105
/Users/owen/Sites/footy-finance/vendor/laravel/framework/src/Illuminate/Foundation/helpers.php:263
/Users/owen/Sites/footy-finance/vendor/laravel/dusk/src/TestCase.php:203
/Users/owen/Sites/footy-finance/vendor/laravel/dusk/src/TestCase.php:40
I've had a look at line 40 of TestCase.php and its
public function baseUrl()
{
return config('app.url');
}
So it does look like something to do with the global config helper anybody have any ideas?
I'm running
PHP 7.0.14
Laravel/Framework 5.4.8
Laravel/Dusk 1.0.5
The full composer.lock can be seen https://gist.github.com/OwenMelbz/c05172b33f6eb4483e37a56469b53722
Fingers crossed you guys have some ideas!
Cheers :)
I had this error in the log
Class config does not exist
the problem with me was that in the .env file I had set a configuration variable in the following way:
APP_NAME=Application Name
note the space. When I changed it to this:
APP_NAME="Application Name"
the problem got fixed
The issue is with .env file
App_Name
in the original file its written this way>>> APP_NAME=Application Name
Make it like this APP_NAME="Application Name"
In my case, this solution works:
1) Remove all contents of the bootstrap/cache folder
2) Run the composer dump command
For anybody else who has had this issue.
I had prefer stable set in the composer file, which installed PHPUnit 6.
This was "made stable today" - thus it installed during a composer update.
Downgrading to PHPUnit 5 fixes the issue - so was bad timing starting it today.
I just ran into the the same issue, in my case the .env was all clean, no unwrapped empty spaces.
This error message can also occur when writting/debugging a test case, using the setup() method in that test, forgetting to call parent::setup() as the first statement in that function.
protected $stuf;
function setup() {
parent::setup();
$this->stuf = 'stuf';
}
I found very useful info here on what else could happen when you're getting this error message.
I've also had this issue. For me it was caused by calling the config() function inside a dataProvider method. DataProviders are called before the createApplication() method initialises the application, and populates the DI container. Hence config() fails because the app('config') call in the helper function can't resolve the config class from the container.
I'm very late for the party here but for anyone experiencing the same issue with Laravel's unit test and none of the above solutions work, you can look into mine and see if this might help.
In my case, I was trying to call a method that will remove all the test keys that persisted in my Redis database when I run the unit test. The method is called in the tearDown method of the class. The error occurs because the parent constructor is called before the actual tearDown code is executed. That's the reason why I'm having the error.
Instead of this one......
/**
* tearDown is executed after test stub
*/
protected function tearDown()
{
parent::tearDown();
$this->deleteTestKeys();
}
Change it to this one...
protected function tearDown()
{
$this->deleteTestKeys();
parent::tearDown();
}
In this case, the class' is not totally destroyed yet and the Laravel's config method will get called accordingly.
I had this in a Lumen application today. After some investigation and playing around, I found that it was because in PHPStorm it was adding the --no-configuration option onto the phpunit command because I hadn't configured my PHPUnit setup for the project in the IDE.
I corrected that by clicking 'Run > Edit Configurations' and then under 'Defaults > PHPUnit' click the little button to the far right of the 'Use alternative configuration file:' option and set the 'Default configuration file:' to the full path to your project's phpunit.xml.
Hope this helps!
I saw this error after following some dodgy installation instructions for a third party module, which said to register a service provider in bootstrap/app.php
$app->singleton(...);
$app->singleton(...);
$app->register(\Third\Party\ServiceProvider::class);
This caused $this->app['config'] to generate the error BindingResolutionException: Target class [config] does not exist.
I fixed it by putting it in config/app.php, where it belongs:
/*
* Package Service Providers...
*/
Third\Party\ServiceProvider::class,
I am trying to set-up acceptance tests using Codeception in Yii2.
So far so good with regards to installation, but I am having a route issue.
When I do:
codeception run acceptance
I get this feedback:
1) Failed to ensure login page works in LoginCept (./acceptance/LoginCept.php)
Step I fill field "input[name="LoginForm[username]"]",""
Fail Form field by Label or CSS element with 'input[name="LoginForm[username]"]' was not found.
Scenario Steps:
3. $I->fillField("input[name="LoginForm[username]"]","")
2. // I am going to submit login form with no data
1. $I->amOnPage("/backend/web/index-test.php/")
The input with name LoginForm[username] exists on the page, but apparantly Codeception is not getting the correct page.
Should /backend/web/index-test.php also have the approot path in it? When I request approot/backend/web/index-test.php in the browser it all works fine.
Thanks for any pointers.
Alex
UPDATE: hereby the contents of acceptance.suite.yml:
# Codeception Test Suite Configuration
# suite for acceptance tests.
# perform tests in browser using the Selenium-like tools.
# powered by Mink (http://mink.behat.org).
# (tip: that's what your customer will see).
# (tip: test your ajax and javascript by one of Mink drivers).
# RUN `build` COMMAND AFTER ADDING/REMOVING MODULES.
class_name: AcceptanceTester
modules:
enabled:
- PhpBrowser
- tests\codeception\common\_support\FixtureHelper
# you can use WebDriver instead of PhpBrowser to test javascript and ajax.
# This will require you to install selenium. See http://codeception.com/docs/04-AcceptanceTests#Selenium
# "restart" option is used by the WebDriver to start each time per test-file new session and cookies,
# it is useful if you want to login in your app in each test.
# - WebDriver
config:
PhpBrowser:
# PLEASE ADJUST IT TO THE ACTUAL ENTRY POINT WITHOUT PATH INFO
url: http://localhost:8080
# WebDriver:
# url: http://localhost:8080
# browser: firefox
# restart: true
UPDATE BASED ON COMMENTS BELOW:
I'm lost. I tried hardcoding the path, and even tried hardcoding the localhost URL, but then I get this response:
$I->amOnPage("/backend/web/index-test.php/localhost/www/yii2KickDish/backend/web")
which clearly is a bogus URL....so how can I get Codeception to resolve to the right location?
I got the same issue.
Seems like the "amOnPage" method build into the AcceptanceTesterActions trait isnt working well with yii2 url pattern.
That's how i solved this.
Create a class MainTester extending from AcceptanceTester
namespace tests\codeception\master\Step\Acceptance;
use tests\codeception\master\AcceptanceTester;
class MainTester extends AcceptanceTester
{
public function amOnPage($url)
{
$page = \Yii::$app->getUrlManager()->createUrl($url);
return parent::amOnPage($page);
}
}
Then in my Cest class
use tests\codeception\master\Step\Acceptance\MainTester as AcceptanceTester;
class TestClassCest
{
public function testMethod(AcceptanceTester $I)
{
$I->amOnPage('/example/something');
}
}
I am new to Codeception and I am trying to test my web service which I have built using Laravel 5. I am following the guide here.
So I created a suite called api first:
codecept generate:suite api
api.suite.yml
class_name: ApiTester
modules:
enabled:
- REST:
url: http://localhost:8000/api/
depends: Laravel5
AuthenticateUserCept.php
<?php
$I = new ApiTester($scenario);
$I->wantTo('authenticate a user');
$I->haveHttpHeader('Content-Type', 'application/x-www-form-urlencoded');
$I->sendPOST('/authenticate', [
'username' => 'archive',
'email' => 'admin#admin.com',
'password' => 'password'
]);
$I->seeResponseCodeIs(200);
$I->seeResponseIsJson();
I have already tried this using Postman and it runs fine. The /authenticate route takes the 3 parameters and spits out a JWT token happily but I cannot make it work with Codeception.
1) Failed to authenticate a user in AuthenticateUserCept (tests/api//AuthenticateUserCept.php)
Step I see response code is 200
Fail Failed asserting that 500 matches expected 200.
Scenario Steps:
3. $I->seeResponseCodeIs(200)
2. $I->sendPOST("/authenticate",{"username":"archive","email":"admin#admin.com","password":"password"})
1. $I->haveHttpHeader("Content-Type","application/x-www-form-urlencoded")
Where am I going wrong? And moreover it is still blurry to me that how do I refactor this particular test in order to have a JWT for other requests that I make. Any help is appreciated.
EDIT changes in api.suite.yml
class_name: ApiTester
modules:
enabled:
- REST:
url: http://localhost:8000/api/
depends: Laravel5
config:
Laravel5:
environment_file: .env.testing
Before I answer, some comments:
on the enabled line in api.suite.yml I include: enabled: [PhpBrowser, REST, ApiHelper, Asserts].
The url in the REST line doesn't make any difference for the API suite, it's usually only used for the acceptance suite. This should be doing everything via the routes directly into your code instead of using phpbrowser mode or similar where it's making HTTP calls.
Instead of using the PhpBrowser mode you can use the Laravel5 mode -- the former will send requests through the middleware, the Laravel5 mode won't put requests through the middleware if the APP_ENV is set to testing.
In terms of how to fix this, I would start by trying to figure out what the code is doing to spit out the 500 error. One thing that you might do is try to insert some debug/log lines in your code. Another thing is to put in a line like this into the test case:
$I->seeResponseContains('blablabla');
Of course that will fail, but it will produce an output in the tests/_output directory so that you can see exactly what response your code gave back along with the 500 error (of course you need to do this the step before testing for the 200 condition, otherwise your test will bail out at that step).
One common cause of this type of error in test cases is validation applied to a custom Request class that's passed in to your controller method. If that validation fails then the controller method doesn't get hit and you will get a 500 error without being able to debug it in the controller. I consider custom Request classes of this type to be a bad idea.
Another possible line of enquiry is to build a functional test that tests the same route. Functional tests via the Laravel5 helper module are usually easier to get working and get debugged. Once you have a basic functional test in place and resolve any problems that you hit getting that going then it's easier to get the API tests working.
Does anyone know how to successfully configure Mink to work with Behat? In case if anyone doesn't know, Behat is a BDD(Behaviour-Driven Development) framework for PHP and Mink provides a browser emulators abstraction layer to test with.
You can find out more about Behat at http://behat.org/ and Mink at https://github.com/Behat/Mink or http://www.knplabs.com/fr/blog/one-mink-to-rule-them-all
Basically i followed the instructions at http://www.knplabs.com/fr/blog/one-mink-to-rule-them-all to configure my Mink to work with my Behat. My behat.yml, the one located inside the Behat folder, not the Mink folder, is as follows:
default:
paths:
features: %%BEHAT_CONFIG_PATH%%/features
formatter:
name: progress
pretty:
formatter:
name: pretty
parameters:
multiline_arguments: false
default:
environment:
parameters:
start_url: http://localhost/
imports:
- mink/behat.yml
I also have the following code in my features/support/boostrap.php
require_once 'mink/autoload.php';
However, having the following code in my features/support/env.php
$world->client = new \Goutte\Client;
would give me a PHP Fatal error: Class 'Goutte\Client' not found in terminal(OSX) when i use the behat command. This happens even if i have the goutte.phar inside my behat/Mink/Vendor/Goutte folder.
Hope anyone can enlighten me on where i went wrong and if there was any part in the question where I wasn't being clear about it, do let me know. Thanks a lot.
Here it is: https://github.com/knplabs/mink-demo ;-)
Basically, with Mink, you don't need to create or require Goutte client it's done by Mink automatically. Your $world was also enhanced and now you're able to get mink session inside step definitions:
$downloadsLink = $world->getSession()->getPage()->findLink('downloads');
Also, you've forgot to include PHPUnit!
See mink-demo for getting great example ;-)