How to use only nth line of DataProvider in Codeception? - codeception

i am using Codeception for testing. I am using data providers and everything is working well. But i need to be able just say "i need to start only line 1 from data provider", how can i do it?
protected function pageProviderTest(){
return[
['version' => 1],
['version' => 2],
['version' => 3],
];
}
/**
* #param WebdriverTester $I
* #dataProvider pageProviderTest
*/
public function test1(WebdriverTester $I, Example $example){
$I->see($example['version']);
}
So for example, now i only want to test if test see "1". Other test i dont want even start.

So i find answer.
You have to make new Example object and give him parameter which you want to use, so in this situation it will be look´s something like:
private $testingData;
public function __construct()
{
$this->testingData = new Example(pageProviderTest[0]); // enter the num of line in field
}
protected function pageProviderTest(){
return[
['version' => 1],
['version' => 2],
['version' => 3],
];
}
/**
* #param WebdriverTester $I
*
*/
protected function test1(WebdriverTester $I, Example $example){
$I->see($example['version']);
}
public function startTest(WebdriverTester $I){
test1($I, $this->testingData);
}

Related

Symfony, PHPUnit : Client Webdriver Authentication

I need to authenticate my WebDriver Client for functional tests.
For example,
In my integration tests, i'm doing something like that :
namespace Tests\Controller;
use App\Entity\Donor;
use App\Entity\User;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Tools\SchemaTool;
use SebastianBergmann\Type\RuntimeException;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class DonorTest extends WebTestCase
{
private static $client;
/**
* #var EntityManager
*/
private $entityManager;
/**
* #var SchemaTool
*/
private $schemaTool;
public function __construct(?string $name = null, array $data = [], string $dataName = '')
{
parent::__construct($name, $data, $dataName);
static::ensureKernelShutdown();
if (!self::$client) {
self::$client = static::createClient([], [
'PHP_AUTH_USER' => 'Same Old User',
'PHP_AUTH_PW' => 'Same Old Password',
]);
}
$this->entityManager = self::bootKernel()
->getContainer()
->get('doctrine')
->getManager();
$this->schemaTool = new SchemaTool($this->entityManager);
/** Safeguard */
$connection = $this->entityManager->getConnection()->getParams();
if ($connection['driver'] != 'pdo_sqlite' || $connection['path'] != '/tmp/test_db.sqlite') {
throw new RuntimeException('Wrong database, darling ! Please set-up your testing database correctly. See /config/packages/test/doctrine.yaml and /tests/README.md');
}
}
I'm just passing the credentials in paramaters, and it works.
But, in my functional tests, i'm using the WebDriver. It didn't accept credentials in arguments :
<?php
namespace App\Tests\Functional\Entities\Donor;
use App\Entity\Donor;
use App\Tests\Functional\Helpers\Carrier\CarrierHelper;
use App\Tests\Functional\Helpers\Donor\DonorHelper;
use Doctrine\ORM\EntityManager;
use Facebook\WebDriver\WebDriverBy;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Panther\PantherTestCase;
use Symfony\Component\Panther\Client;
class DonorTest extends PantherTestCase
{
/**
* #var EntityManager
*/
private $entityManager;
/**
* #var CarrierHelper
*/
private $helper;
/**
* #var Client
*/
private $client;
public function __construct(?string $name = null, array $data = [], string $dataName = '')
{
parent::__construct($name, $data, $dataName);
$this->entityManager = self::bootKernel()
->getContainer()
->get('doctrine')
->getManager();
$this->helper = new DonorHelper();
}
public static function setUpBeforeClass(): void
{
// Do something
}
public function setUp(): void
{
parent::setUp(); // TODO: Change the autogenerated stub
$this->client = Client::createChromeClient();
$this->client->manage()->window()->maximize();
}
I can't pass any login arguments in createChromeClient() method.
I think i have to play with cookies in cookieJar, or token, but i don't know how.
Feel free to ask me my ahtentication method, but i've followed the documentation :
https://symfony.com/doc/current/security/form_login_setup.html
EDIT
I've just tried something else. Log in with my browser, for generate a cookie, and tried to handcraft an other with same PHPSESSID
public function setUp(): void
{
parent::setUp(); // TODO: Change the autogenerated stub
$this->client = Client::createChromeClient();
$this->client->manage()->window()->maximize();
$cookie = new Cookie('PHPSESSID', 'pafvg5nommcooa60q14nqhool0');
$cookie->setDomain('127.0.0.1');
$cookie->setHttpOnly(true);
$cookie->setSecure(false);
$cookie->setPath('/');
$this->client->manage()->addCookie($cookie);
}
But get this error :
Facebook\WebDriver\Exception\InvalidCookieDomainException: invalid cookie domain
Domain is good, same as my web browser.
I will update as my investigations progressed.
EDIT 2
Ok... Got It.
According to this thread : Unable to set cookies in Selenium Webdriver
For setting-up cookie['domain'], you have to request firstly on the domain, THEN set-up the cookie...
SO, this is almost working :
public function setUp(): void
{
parent::setUp(); // TODO: Change the autogenerated stub
$this->client = Client::createChromeClient();
$this->client->manage()->window()->maximize();
$this->client->request('GET', 'http://127.0.0.1/randompage');
$handcookie = Cookie::createFromArray([
'name' => 'PHPSESSID',
'value' => 'pcvbf3sjlla16rfb1b1274qk01',
'domain' => '127.0.0.1',
'path' => '/'
]);
$this->client->manage()->addCookie($handcookie);
}
Next step : Find a way to generate a permanent cookie, without lifetime.
I think nobody will read this but i will update it in case someone else gets stuck.

Convert local scope to global scope or query by parent attribute

I want to retrieve all comments which belong to active posts.
I have a local scope on my Posts model looking like this.
public function scopePublic($query) {
return $query->whereHas('post', function ($q) {
$q->where('is_public', true);
});
}
Which works fine, but breaks with PHP message: PHP Fatal error: Allowed memory size of X bytes exhausted as soon as I want to convert it to a global scope like this:
static::addGlobalScope('is_public', function (Builder $builder) {
return $builder->whereHas('post', function ($q) {
$q->where('is_public', true);
});
});
My end goal is for all comment queries only to show public comments, unless I specifically ask not to.
I've been through quite a few solutions. I've tried joining the post on the comments, and I tried adding a sub-select to no luck.
$builder->addSelect(['is_public' => Post::select('is_private')
->whereColumn('id', 'comment.post_id')->limit(1)
]);
$builder->join('posts','posts.id','=','comments.post_id')
->where('comments.is_private', false);
Make a new class PublicScope
use Illuminate\Database\Eloquent\Scope;
class CommentPublicScope implements Scope
{
/**
* Apply the scope to a given Eloquent query builder.
*
* #param \Illuminate\Database\Eloquent\Builder $builder
* #param \Illuminate\Database\Eloquent\Model $model
* #return void
*/
public function apply(Builder $builder, Model $model)
{
$builder->whereHas('post', function ($q) {
$q->where('is_public', true);
});
}
}
Then you can add the global scope
Class Comment extends Model
{
protected static function boot()
{
parent::boot();
static::addGlobalScope(new CommentPublicScope);
}
}

How to fix this DEPRECATION error in codeception?

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');
}

How to validate two dimensional array in Yii2

How to validate two dimensional array in Yii2.
passenger[0][name] = bell
passenger[0][email] = myemail#test.com
passenger[1][name] = carson123
passenger[1][email] = carson###test.com
how to validate the name and email in this array
Thanks
Probably the most clean solution for validating 2-dimensional array is treating this as array of models. So each array with set of email and name data should be validated separately.
class Passenger extends ActiveRecord {
public function rules() {
return [
[['email', 'name'], 'required'],
[['email'], 'email'],
];
}
}
class PassengersForm extends Model {
/**
* #var Passenger[]
*/
private $passengersModels = [];
public function loadPassengersData($passengersData) {
$this->passengersModels = [];
foreach ($passengersData as $passengerData) {
$model = new Passenger();
$model->setAttributes($passengerData);
$this->passengersModels[] = $model;
}
return !empty($this->passengers);
}
public function validatePassengers() {
foreach ($this->passengersModels as $passenger) {
if (!$passenger->validate()) {
$this->addErrors($passenger->getErrors());
return false;
}
}
return true;
}
}
And in controller:
$model = new PassengersForm();
$model->loadPassengersData(\Yii::$app->request->post('passenger', []));
$isValid = $model->validatePassengers();
You may also use DynamicModel instead of creating Passanger model if you're using it only for validation.
Alternatively you could just create your own validator and use it for each element of array:
public function rules() {
return [
[['passengers'], 'each', 'rule' => [PassengerDataValidator::class]],
];
}
You may also want to read Collecting tabular input section in guide (unfortunately it is still incomplete).

Zend Framework 2 - Service method require as parameter InputFilter

I have a bit OOD question.
I have service:
namespace Front\Service\Course;
use Front\ORM\EntityManagerAwareInterface;
use Zend\Http\Request;
use Zend\InputFilter\InputFilter;
use Front\InputFilter\Course\CreateFilter;
class Create implements EntityManagerAwareInterface
{
/**
* #var \Doctrine\Orm\EntityManager
*/
protected $entityManager = null;
public function create(CreateFilter $createFilter)
{
if (!$createFilter->isValid()) return false;
/* #var $courseRepository \Front\Repositories\CourseRepository */
$courseRepository = $this->getEntityManager()->getRepository('Front\Entities\Course');
$course = $courseRepository->findByName($createFilter->getCourse());
}
/* (non-PHPdoc)
* #see \Front\ORM\EntityManagerAwareInterface::getEntityManager()
*/
public function getEntityManager()
{
return $this->entityManager;
}
/* (non-PHPdoc)
* #see \Front\ORM\EntityManagerAwareInterface::setEntityManager()
*/
public function setEntityManager(\Doctrine\ORM\EntityManager $entityManager)
{
$this->entityManager = $entityManager;
return $this;
}
}
And controller :
class CreateController extends \Zend\Mvc\Controller\AbstractController
{
public function onDispatch(MvcEvent $e)
{
$jsonModel = new JsonModel();
/* #var $courseCreateService \Front\Service\Course\Create */
$courseCreateService = $this->getServiceLocator()->get('Front\Service\Course\Create');
$courseCreateFilter = new CreateFilter();
$courseCreateFilter->setData($this->params()->fromPost());
if (!$courseCreateFilter->isValid()) {
$jsonModel->setVariable('status', 0);
$jsonModel->setVariable('message', $courseCreateFilter->getMessages());
return;
}
$courseCreateService->create($courseCreateFilter);
}
}
By service method declaration :
public function create(CreateFilter $createFilter)
i force user of the Service to use CreateFilter container which derived from Zend/InputFilter every time when he want to create new Course.
My question is: Might it be better when i will send to the service layer not the Typed object but simple value?
On example in my case it is might looks like:
public function create($courseName)
My CreateFilter looks like:
class CreateFilter extends InputFilter
{
public function __construct()
{
$input = new Input('name');
$validatorChain = new ValidatorChain();
$validatorChain->addValidator(new StringLength(array('max'=>60)))
->addValidator(new NotEmpty());
$input->setRequired(true)->setValidatorChain($validatorChain);
$this->add($input);
}
/**
* #return string | null
*/
public function getCourse()
{
return $this->getValue('name');
}
}
If you provide a concrete class name as you're doing now, you're forever tied to a concrete implementation of the class or one derived from it. If you decide later that you want to use a different class entirely, you have to refactor your service class code, whereas with an interface, you only need to implement it in your new class and your service will continue to work without any changes.
Without any interface at all, your service class would have to do extra checks to first see if it's an object and then if it implements the method you're expecting before it can even begin doing its job. By requiring an interface you remove the uncertainty, and negate the need for checks.
By providing an interface you create a contract between your methods and the classes they're expecting as arguments without restricting which classes may enter into the contract. All in all, contract by interface is preferable to contract by class name, but both are preferable to no contract at all.
I usually bind my entities to my form, so they are populated with the data from the form. This way, you inject the entity to your service and imho that's much cleaner. The service should not be aware of how you got your data.
My "admin" controller for an entity Bar usually is injected with three objects: the repository (to query objects), the service (to persist/update/delete objects) and the form (to modify objects for the user). A standard controller is then very CRUD based and only pushes entities to the service layer:
<?php
namespace Foo\Controller;
use Foo\Repository\Bar as Repository;
use Foo\Form\Bar as Form;
use Foo\Service\Bar as Service;
use Foo\Entity\Bar as Entity;
use Foo\Options\ModuleOptions;
use Zend\Mvc\Controller\AbstractActionController;
class BarController extends AbstractActionController
{
/**
* #var Repository
*/
protected $repository;
/**
* #var Service
*/
protected $service;
/**
* #var Form
*/
protected $form;
/**
* #var ModuleOptions
*/
protected $options;
public function __construct(Repository $repository, Service $service, Form $form, ModuleOptions $options = null)
{
$this->repository = $repository;
$this->service = $service;
$this->form = $form;
if (null !== $options) {
$this->options = $options;
}
}
public function getService()
{
return $this->service;
}
public function getRepository()
{
return $this->repository;
}
public function getForm()
{
return $this->form;
}
public function getOptions()
{
if (null === $this->options) {
$this->options = new ModuleOptions;
}
return $this->options;
}
public function indexAction()
{
$bars = $this->getRepository()->findAll();
return array(
'bars' => $bars,
);
}
public function viewAction()
{
$bar = $this->getBar();
return array(
'bar' => $bar,
);
}
public function createAction()
{
$bar = $this->getBar(true);
$form = $this->getForm();
$form->bind($bar);
if ($this->getRequest()->isPost()) {
$data = $this->getRequest()->getPost();
$form->setData($data);
if ($form->isValid()) {
// Bar is populated with form data
$this->getService()->create($bar);
return $this->redirect()->toRoute('bar/view', array(
'bar' => $bar->getId(),
));
}
}
return array(
'form' => $form,
);
}
public function updateAction()
{
$bar = $this->getBar();
$form = $this->getForm();
$form->bind($bar);
if ($this->getRequest()->isPost()) {
$data = $this->getRequest()->getPost();
$form->setData($data);
if ($form->isValid()) {
$this->getService()->update($bar);
return $this->redirect()->toRoute('bar/view', array(
'bar' => $bar->getId(),
));
}
}
return array(
'bar' => $bar,
'form' => $form,
);
}
public function deleteAction()
{
if (!$this->getRequest()->isPost()) {
$this->getRequest()->setStatusCode(404);
return;
}
$bar = $this->getBar();
$this->getService()->delete($bar);
return $this->redirect()->toRoute('bar');
}
protected function getBar($create = false)
{
if (true === $create) {
$bar = new Entity;
return $bar;
}
$id = $this->params('bar');
$bar = $this->getRepository()->find($id);
if (null === $bar) {
throw new Exception\BarNotFoundException(sprintf(
'Bar with id "%s" not found', $id
));
}
return $bar;
}
}
I made a gist file on Github with this full code (it's better readable) and the service. The service relies on the interface, so you can even swap out the entity object by another one having the same interface.
Check the full thing out here: https://gist.github.com/juriansluiman/5472787
Thanks all for answering, owing to answers and analyzing, i have reached conclusion which most applicable for my situation. I agree that Service in my case should not wait concrete object, it is should wait an abstraction with getCourse method.
And i completely agree with "Crisp" answer:
All in all, contract by interface is preferable to contract by class name, but both are preferable to no contract at all.
So i need to extract Interface with one method
getCourse
or
getName
, and remove
if (!$createFilter->isValid()) return false;
so Interface:
interface CourseInterface
{
/**
* #return String
**/
public function getName();
}
and Service:
class Create implements EntityManagerAwareInterface
{
/**
* #var \Doctrine\Orm\EntityManager
*/
protected $entityManager = null;
/**
* #param CourseInterface $course
* #param UserInterface $creator
*/
public function create(CourseInterface $course)
{
$courseEntity = new Course();
$courseEntity->setName($course->getName());
$this->entityManager->persist($courseEntity);
$this->entityManager->flush();
.....
Thanks all.