SlideShare ist ein Scribd-Unternehmen logo
1 von 49
Downloaden Sie, um offline zu lesen
DIVERSIFIED APPLICATION
TESTING
BASED ON A SYLIUS PROJECT
Łukasz Chruściel
AGENDA
• Sylius in a nutshell
• Tests and their types
• PHPSpec usage for unit testing
• API testing with ApiTestCase
• New approach to Behat
• Summary
SYLIUS
IN A NUTSHELL
WHAT IS SYLIUS?
330+ contributors 10k+ contributions
5 years of development
Still in dev
A few revolutions behind us
60+ packages Full stack BDD
OSS Project
E-commerce platform
REVOLUTIONS?
Fixtures system
Shop
Admin panel
Repositories
Factories
Translations
New approach of Behat
User handling
Products
Checkout flow
State machines
HOW DID WE HANDLE IT?
TESTS
TYPES OFTESTS
• Unit
• Integration
• Functional
• Stress
• Performance
• Usability
• Acceptance
• Regression
• API
• GUI
TYPES OFTESTS
• Unit
• Integration
• Functional
• Acceptance
• API
• GUI
PHPSPECTOTHE RESCUE!
UnitTests?
EASYTO UNDERSTAND
final class TypicalCalculatorSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->shouldHaveType(Calculator::class);
}
function it_adds_two_numbers()
{
$this->add(2, 4)->shouldReturn(6);
}
}
AcmeAppBundleCalculatorTypicalCalculator
25 ✔ is initializable
35 ✔ adds two numbers
1 specs
3 examples (3 passed)
11ms
final class TypicalCalculator
{
public function add($a, $b)
{
return $a + $b;
}
}
SOMETHING MORE
SOPHISTICATED
class AverageRatingCalculator implements ReviewableRatingCalculatorInterface
{
public function calculate(ReviewableInterface $reviewable)
{
$sum = 0;
$reviewsNumber = 0;
$reviews = $reviewable->getReviews();
foreach ($reviews as $review) {
if (ReviewInterface::STATUS_ACCEPTED === $review->getStatus()) {
++$reviewsNumber;
$sum += $review->getRating();
}
}
return 0 !== $reviewsNumber ? $sum / $reviewsNumber : 0;
}
}
SOMETHING MORE
SOPHISTICATED
function it_calculates_average_price(
ReviewableInterface $reviewable,
ReviewInterface $review1,
ReviewInterface $review2
) {
$reviewable->getReviews()->willReturn([$review1, $review2]);
$review1
->getStatus()
->willReturn(ReviewInterface::STATUS_ACCEPTED);
$review2
->getStatus()
->willReturn(ReviewInterface::STATUS_ACCEPTED);
$review1->getRating()->willReturn(4);
$review2->getRating()->willReturn(5);
$this->calculate($reviewable)->shouldReturn(4.5);
}
SOMETHING MORE
SOPHISTICATED
function it_returns_zero_if_given_reviewable_object_has_no_reviews(
ReviewableInterface $reviewable
){
$reviewable->getReviews()->willReturn([]));
$this->calculate($reviewable)->shouldReturn(0);
}
SOMETHING MORE
SOPHISTICATED
function it_returns_zero_if_given_reviewable_object_has_reviews_but_none_of_them_is_accepted(
ReviewableInterface $reviewable,
ReviewInterface $review
) {
$reviewable->getReviews()->willReturn([$review]);
$review->getStatus()->willReturn(ReviewInterface::STATUS_NEW);
$this->calculate($reviewable)->shouldReturn(0);
}
SOMETHING MORE
SOPHISTICATED
class AverageRatingCalculator implements ReviewableRatingCalculatorInterface
{
public function calculate(ReviewableInterface $reviewable)
{
$sum = 0;
$reviewsNumber = 0;
$reviews = $reviewable->getReviews();
foreach ($reviews as $review) {
if (ReviewInterface::STATUS_ACCEPTED === $review->getStatus()) {
++$reviewsNumber;
$sum += $review->getRating();
}
}
return 0 !== $reviewsNumber ? $sum / $reviewsNumber : 0;
}
}
COLLABORATORS?
namespace SyliusBundleOrderBundleNumberAssigner;
final class OrderNumberAssigner implements OrderNumberAssignerInterface
{
private $numberGenerator;
public function __construct(OrderNumberGeneratorInterface $numberGenerator)
{
$this->numberGenerator = $numberGenerator;
}
public function assignNumber(OrderInterface $order)
{
if (null !== $order->getNumber()) {
return;
}
$order->setNumber($this->numberGenerator->generate($order));
}
}
COLLABORATORS?
namespace specSyliusBundleOrderBundleNumberAssigner;
final class OrderNumberAssignerSpec extends ObjectBehavior
{
function let(OrderNumberGeneratorInterface $numberGenerator)
{
$this->beConstructedWith($numberGenerator);
}
function it_assigns_number_to_order(
OrderInterface $order,
OrderNumberGeneratorInterface $numberGenerator
) {
$order->getNumber()->willReturn(null);
$numberGenerator->generate($order)->willReturn('00000007');
$order->setNumber('00000007')->shouldBeCalled();
$this->assignNumber($order);
}
function it_does_not_assign_number_to_order_with_number(
OrderInterface $order,
OrderNumberGeneratorInterface $numberGenerator
) {
$order->getNumber()->willReturn('00000007');
$numberGenerator->generate($order)->shouldNotBeCalled();
$order->setNumber(Argument::any())->shouldNotBeCalled();
$this->assignNumber($order);
}
}
WHAT DO WETEST WITH
PHPSPEC?
*Although we don’t spec repositories, nor forms.
EVERYTHING*
„BUT IT IS HARD/IMPOSSIBLE TO SPEC MY CODE!”
THEN SOMETHING SMELLS
REALLY, REALLY BAD
APITESTING WITH
APITESTCASE
JSON?
WHAT DO WE HAVE?
URL:
Raw data to send:
Expected response:
POST /api/countries/
{"code": "BE"}
{
"id": 1,
"code": "BE"
}
namespace SyliusTestController;
class CountryApiTest extends JsonApiTestCase
{
public function testCreateCountryResponse()
{
$data =
<<<EOT
{
"code": "BE"
}
EOT;
$this->client->request('POST', '/api/countries/', [], [], [], $data);
$response = $this->client->getResponse();
$this->assertResponse($response, 'country/create_response', Response::HTTP_CREATED);
}
}
// test/Responses/Expected/country/create_response.json
{
"id": @integer@,
"code": "BE"
}
XML?
WHAT DO WE HAVE?
URL:
Expected response:
GET /sitemap.xml
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>http://sylius.org/products/mug-star-wars</loc>
<lastmod>2015-10-10T00:00:00+02:00</lastmod>
<changefreq>always</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>http://sylius.org/products/mug-lotr</loc>
<lastmod>2015-10-04T00:00:00+02:00</lastmod>
<changefreq>always</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>http://sylius.org/products/mug-breaking-bad</loc>
<lastmod>2015-10-05T00:00:00+02:00</lastmod>
<changefreq>always</changefreq>
<priority>0.5</priority>
</url>
</urlset>
// test/Responses/Expected/sitemap/show_sitemap.xml
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>http://sylius.org/products/mug-star-wars</loc>
<lastmod>@string@.isDateTime()</lastmod>
<changefreq>always</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>http://sylius.org/products/mug-lotr</loc>
<lastmod>@string@.isDateTime()</lastmod>
<changefreq>always</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>http://sylius.org/products/mug-breaking-bad</loc>
<lastmod>@string@.isDateTime()</lastmod>
<changefreq>always</changefreq>
<priority>0.5</priority>
</url>
</urlset>
namespace SyliusTestController;
class SitemapControllerApiTest extends XmlApiTestCase
{
public function testShowActionResponse()
{
$this->loadFixturesFromFile('resources/product.yml');
$this->client->request('GET', '/sitemap.xml');
$response = $this->client->getResponse();
$this->assertResponse($response, 'sitemap/show_sitemap');
}
}
HELPS IN DEBUGGING
1) SyliusTestsControllerOauthTokenApiTest::it_provides_an_access_token
"3600" does not match "7200".
@@ -1,7 +1,7 @@
{
- "expires_in": 7200,
+ "expires_in": 3600,
"token_type": "bearer",
"scope": null,
}
NEW APPROACHTO BEHAT
GOOD, OLD BEHAT
WHAT WASTHAT?
Uber Behat contexts
800+ lines of code
Many ancestors
Each Behat context got an `F` degree on scrutinizer
WAS IT WRONG?
/**
* @Given /^I am on the page of ([^""(w)]*) "([^""]*)"$/
* @Given /^I go to the page of ([^""(w)]*) "([^""]*)"$/
*/
public function iAmOnTheResourcePageByName($type, $name)
{
if ('country' === $type) {
$this->iAmOnTheCountryPageByName($name);
return;
}
$this->iAmOnTheResourcePage($type, 'name', $name);
}
WAS IT WRONG?
IT WAS TERRIBLE!
SOLUTION?
STYLE
@promotions
Feature: Checkout fixed discount promotions
In order to handle product promotions
As a store owner
I want to apply promotion discounts during checkout
Background:
Given store has default configuration
And the following countries exist:
| name |
| Germany |
| Poland |
And there are following taxonomies defined:
| name |
| Category |
And taxonomy "Category" has following taxons:
| Clothing > DebianT-Shirts |
And the following products exist:
| name | price | taxons |
| Woody | 125 | DebianT-Shirts |
And the following promotions exist:
| name | description |
| 3 items | Discount for orders with at least 3 items |
And all products are assigned to the default channel
And all promotions are assigned to the default channel
And promotion "3 items" has following rules defined:
| type | configuration |
| Item count | Count: 3,Equal: true |
And promotion "3 items" has following actions defined:
| type | configuration |
| Fixed discount | Amount: 15 |
And I am logged in as user "klaus@example.com"
Scenario: Fixed discount promotion is applied when the cart
has the required amount
Given I am on the store homepage
When I add product "Woody" to cart, with quantity "3"
Then I should be on the cart summary page
And "Promotion total: -€40.00" should appear on the page
And "Grand total: €295.00" should appear on the page
@receiving_discount
Feature: Receiving fixed discount on cart
In order to pay proper amount while buying promoted goods
As aVisitor
I want to have promotions applied to my cart
Background:
Given the store operates on a single channel in "France"
And the store has a product "PHPT-Shirt" priced at "€100.00"
And the store has a product "PHP Mug" priced at "€6.00"
@ui
Scenario: Receiving fixed discount for my cart
Given there is a promotion "Holiday promotion"
And it gives "€10.00" discount to every order
When I add product "PHPT-Shirt" to the cart
Then my cart total should be "€90.00"
And my discount should be "-€10.00"
@receiving_discount
Feature: Receiving percentage discount on shipping
In order to pay decreased amount for shipping
As a Customer
I want to have shipping promotion applied to my cart
Background:
Given the store operates on a single channel in "France"
And the store has "DHL" shipping method with "€10.00" fee
And the store has a product "PHPT-Shirt" priced at "€100.00"
And there is a promotion "Holiday promotion"
And I am a logged in customer
@ui
Scenario: Receiving percentage discount on shipping
Given the promotion gives "20%" discount on shipping to every order
When I add product "PHPT-Shirt" to the cart
And I proceed selecting "DHL" shipping method
Then my cart total should be "€108.00"
And my cart shipping total should be "€8.00"
100% BEHAT
TRANSFORMERS
<?php
final class PromotionContext implements Context
{
/**
* @Then promotion :promotion should still exist in the registry
*/
public function promotionShouldStillExistInTheRegistry(PromotionInterface $promotion)
{
Assert::notNull($this->promotionRepository->find($promotion->getId()));
}
}
<?php
final class PromotionContext implements Context
{
/**
* @Transform :promotion
*/
public function getPromotionByName($promotionName)
{
return $this->promotionRepository->findOneBy(['name' => $promotionName]);
}
}
@domain @ui

Scenario: Being unable to delete a promotion that was applied to an order

When I try to delete a "Christmas sale" promotion

Then I should be notified that it is in use and cannot be deleted

And promotion "Christmas sale" should still exist in the registry
TAGS
namespace SyliusBehatContextDomain;
final class OrderContext implements Context
{
/**
* @When I delete the order :order
*/
public function iDeleteTheOrder(OrderInterface $order)
{
$this->orderRepository->remove($order);
}
}
namespace SyliusBehatContextUi;
final class OrderContext implements Context
{
/**
* @When I delete the order :order
*/
public function iDeleteTheOrder(OrderInterface $order)
{
$this->showPage->open(['id' => $order->getId()]);
$this->showPage->deleteOrder();
}
}
@domain @ui
Scenario: Deleted order should disappear from the registry
When I delete the order "#00000022"
Then I should be notified that it has been successfully deleted
And this order should not exist in the registry
RECOMENDATIONS
Tags steps should be written in such a way
that can be interpreted in many contexts
Do not create a new object withTransformers
Shared storage container to keep data between contexts
Use ubiquitous language
PAGE OBJECT PATTERN
PAGE AS A SERVICE
namespace SyliusBehatPageAdminAccount;
class LoginPage implements LoginPageInterface
{
public function specifyPassword($password)
{
$this->getDocument()->fillField('Password', $password);
}
public function specifyUsername($username)
{
$this->getDocument()->fillField('Username', $username);
}
}
namespace SyliusBehatContextUiAdmin;
final class LoginContext implements Context
{
public function __construct(LoginPageInterface $loginPage)
{
$this->loginPage = $loginPage;
}


/** @When I specify the username as :username */

public function iSpecifyTheUsername($username)
{
$this->loginPage->specifyUsername($username);
}


/** @When I specify the password as :password */

public function iSpecifyThePasswordAs($password)
{
$this->loginPage->specifyPassword($password);
}
}
CONTEXT AS A SERVICE
default:
suites:
ui_administrator_login:
contexts_as_services:
- sylius.behat.context.hook.doctrine_orm
- sylius.behat.context.transform.user
- sylius.behat.context.setup.channel
- sylius.behat.context.setup.admin_user
- sylius.behat.context.setup.user
- sylius.behat.context.ui.admin.login
filters:
tags: "@administrator_login && @ui"
namespace SyliusBehatContextUiAdmin;
final class LoginContext implements Context
{
public function __construct(LoginPageInterface $loginPage)
{
$this->loginPage = $loginPage;
}
/**
* @Given I want to log in
*/
public function iWantToLogIn(){…}
/**
* @When I specify the username as :username
*/
public function iSpecifyTheUsername($username){…}
/**
* @When I specify the password as :password
*/
public function iSpecifyThePasswordAs($password){…}
/**
* @When I log in
*/
public function iLogIn(){…}
}
SERVICES FTW!
SUMMARY
QUESTIONS?
Łukasz Chruściel
@lukaszchrusciel
https://github.com/lchrusciel
Worth to see:
http://www.phpspec.net/en/stable/
http://docs.behat.org/en/v3.0/
https://github.com/Lakion/ApiTestCase
https://github.com/Sylius/Sylius
http://martinfowler.com/bliki/PageObject.html
http://mink.behat.org/en/latest/
https://github.com/FriendsOfBehat

Weitere ähnliche Inhalte

Was ist angesagt?

Mastering Java Bytecode With ASM - 33rd degree, 2012
Mastering Java Bytecode With ASM - 33rd degree, 2012Mastering Java Bytecode With ASM - 33rd degree, 2012
Mastering Java Bytecode With ASM - 33rd degree, 2012
Anton Arhipov
 
ASP.NET Web API
ASP.NET Web APIASP.NET Web API
ASP.NET Web API
habib_786
 

Was ist angesagt? (20)

Integration tests: use the containers, Luke!
Integration tests: use the containers, Luke!Integration tests: use the containers, Luke!
Integration tests: use the containers, Luke!
 
PostgreSQL Tutorial for Beginners | Edureka
PostgreSQL Tutorial for Beginners | EdurekaPostgreSQL Tutorial for Beginners | Edureka
PostgreSQL Tutorial for Beginners | Edureka
 
JUnit 5
JUnit 5JUnit 5
JUnit 5
 
[JWPA-1]의존성 주입(Dependency injection)
[JWPA-1]의존성 주입(Dependency injection)[JWPA-1]의존성 주입(Dependency injection)
[JWPA-1]의존성 주입(Dependency injection)
 
TypeScript: coding JavaScript without the pain
TypeScript: coding JavaScript without the painTypeScript: coding JavaScript without the pain
TypeScript: coding JavaScript without the pain
 
Angular Dependency Injection
Angular Dependency InjectionAngular Dependency Injection
Angular Dependency Injection
 
Open source APM Scouter로 모니터링 잘 하기
Open source APM Scouter로 모니터링 잘 하기Open source APM Scouter로 모니터링 잘 하기
Open source APM Scouter로 모니터링 잘 하기
 
간단한 블로그를 만들며 Django 이해하기
간단한 블로그를 만들며 Django 이해하기간단한 블로그를 만들며 Django 이해하기
간단한 블로그를 만들며 Django 이해하기
 
Whitebox testing of Spring Boot applications
Whitebox testing of Spring Boot applicationsWhitebox testing of Spring Boot applications
Whitebox testing of Spring Boot applications
 
From ActiveRecord to EventSourcing
From ActiveRecord to EventSourcingFrom ActiveRecord to EventSourcing
From ActiveRecord to EventSourcing
 
[pgday.Seoul 2022] POSTGRES 테스트코드로 기여하기 - 이동욱
[pgday.Seoul 2022] POSTGRES 테스트코드로 기여하기 - 이동욱[pgday.Seoul 2022] POSTGRES 테스트코드로 기여하기 - 이동욱
[pgday.Seoul 2022] POSTGRES 테스트코드로 기여하기 - 이동욱
 
Unit Testing with Jest
Unit Testing with JestUnit Testing with Jest
Unit Testing with Jest
 
Mastering Java Bytecode With ASM - 33rd degree, 2012
Mastering Java Bytecode With ASM - 33rd degree, 2012Mastering Java Bytecode With ASM - 33rd degree, 2012
Mastering Java Bytecode With ASM - 33rd degree, 2012
 
Gradle Introduction
Gradle IntroductionGradle Introduction
Gradle Introduction
 
PHP for Adults: Clean Code and Object Calisthenics
PHP for Adults: Clean Code and Object CalisthenicsPHP for Adults: Clean Code and Object Calisthenics
PHP for Adults: Clean Code and Object Calisthenics
 
ASP.NET Web API
ASP.NET Web APIASP.NET Web API
ASP.NET Web API
 
Spring boot introduction
Spring boot introductionSpring boot introduction
Spring boot introduction
 
SymfonyLive Online 2023 - Is SOLID dead? .pdf
SymfonyLive Online 2023 - Is SOLID dead? .pdfSymfonyLive Online 2023 - Is SOLID dead? .pdf
SymfonyLive Online 2023 - Is SOLID dead? .pdf
 
Spring boot jpa
Spring boot jpaSpring boot jpa
Spring boot jpa
 
TypeScript for Java Developers
TypeScript for Java DevelopersTypeScript for Java Developers
TypeScript for Java Developers
 

Ähnlich wie Diversified application testing based on a Sylius project

How to Sharpen Your Investigative Analysis with PowerPivot
How to Sharpen Your Investigative Analysis with PowerPivotHow to Sharpen Your Investigative Analysis with PowerPivot
How to Sharpen Your Investigative Analysis with PowerPivot
Carmen Mardiros
 
In memory OLAP engine
In memory OLAP engineIn memory OLAP engine
In memory OLAP engine
WO Community
 

Ähnlich wie Diversified application testing based on a Sylius project (20)

Building a Pyramid: Symfony Testing Strategies
Building a Pyramid: Symfony Testing StrategiesBuilding a Pyramid: Symfony Testing Strategies
Building a Pyramid: Symfony Testing Strategies
 
Finding the Right Testing Tool for the Job
Finding the Right Testing Tool for the JobFinding the Right Testing Tool for the Job
Finding the Right Testing Tool for the Job
 
How to Sharpen Your Investigative Analysis with PowerPivot
How to Sharpen Your Investigative Analysis with PowerPivotHow to Sharpen Your Investigative Analysis with PowerPivot
How to Sharpen Your Investigative Analysis with PowerPivot
 
Add even richer interaction to your site - plone.patternslib
Add even richer interaction to your site - plone.patternslibAdd even richer interaction to your site - plone.patternslib
Add even richer interaction to your site - plone.patternslib
 
Angularjs
AngularjsAngularjs
Angularjs
 
Velocity 2014: Accelerate Your User Experience With Client-side JavaScript
Velocity 2014: Accelerate Your User Experience With Client-side JavaScriptVelocity 2014: Accelerate Your User Experience With Client-side JavaScript
Velocity 2014: Accelerate Your User Experience With Client-side JavaScript
 
Running Vue Storefront in production (PWA Magento webshop)
Running Vue Storefront in production (PWA Magento webshop)Running Vue Storefront in production (PWA Magento webshop)
Running Vue Storefront in production (PWA Magento webshop)
 
Thinking in swift ppt
Thinking in swift pptThinking in swift ppt
Thinking in swift ppt
 
Continuous Delivery and Rapid Experimentation
Continuous Delivery and Rapid ExperimentationContinuous Delivery and Rapid Experimentation
Continuous Delivery and Rapid Experimentation
 
Swift meetup22june2015
Swift meetup22june2015Swift meetup22june2015
Swift meetup22june2015
 
Wave Analytics: Developing Predictive Business Intelligence Apps
Wave Analytics: Developing Predictive Business Intelligence AppsWave Analytics: Developing Predictive Business Intelligence Apps
Wave Analytics: Developing Predictive Business Intelligence Apps
 
Eventually Elasticsearch: Eventual Consistency in the Real World
Eventually Elasticsearch: Eventual Consistency in the Real WorldEventually Elasticsearch: Eventual Consistency in the Real World
Eventually Elasticsearch: Eventual Consistency in the Real World
 
Optimizely Developer Showcase
Optimizely Developer ShowcaseOptimizely Developer Showcase
Optimizely Developer Showcase
 
In memory OLAP engine
In memory OLAP engineIn memory OLAP engine
In memory OLAP engine
 
DRUPAL AND ELASTICSEARCH
DRUPAL AND ELASTICSEARCHDRUPAL AND ELASTICSEARCH
DRUPAL AND ELASTICSEARCH
 
Cómo tener analíticas en tu app y no volverte loco
Cómo tener analíticas en tu app y no volverte locoCómo tener analíticas en tu app y no volverte loco
Cómo tener analíticas en tu app y no volverte loco
 
Automating Performance Monitoring at Microsoft
Automating Performance Monitoring at MicrosoftAutomating Performance Monitoring at Microsoft
Automating Performance Monitoring at Microsoft
 
App Store Subscriptions - Condensed Edition
App Store Subscriptions - Condensed EditionApp Store Subscriptions - Condensed Edition
App Store Subscriptions - Condensed Edition
 
No REST - Architecting Real-time Bulk Async APIs
No REST - Architecting Real-time Bulk Async APIsNo REST - Architecting Real-time Bulk Async APIs
No REST - Architecting Real-time Bulk Async APIs
 
Prateek dayal backbonerails-110528024926-phpapp02
Prateek dayal backbonerails-110528024926-phpapp02Prateek dayal backbonerails-110528024926-phpapp02
Prateek dayal backbonerails-110528024926-phpapp02
 

Mehr von Łukasz Chruściel

Mehr von Łukasz Chruściel (19)

Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New Features
 
ConFoo 2024 - Need for Speed: Removing speed bumps in API Projects
ConFoo 2024  - Need for Speed: Removing speed bumps in API ProjectsConFoo 2024  - Need for Speed: Removing speed bumps in API Projects
ConFoo 2024 - Need for Speed: Removing speed bumps in API Projects
 
ConFoo 2024 - Sylius 2.0, top-notch eCommerce for customizable solution
ConFoo 2024 - Sylius 2.0, top-notch eCommerce for customizable solutionConFoo 2024 - Sylius 2.0, top-notch eCommerce for customizable solution
ConFoo 2024 - Sylius 2.0, top-notch eCommerce for customizable solution
 
SyliusCon - Typical pitfalls of Sylius development.pdf
SyliusCon - Typical pitfalls of Sylius development.pdfSyliusCon - Typical pitfalls of Sylius development.pdf
SyliusCon - Typical pitfalls of Sylius development.pdf
 
Need for Speed: Removing speed bumps in API Projects
Need for Speed: Removing speed bumps in API ProjectsNeed for Speed: Removing speed bumps in API Projects
Need for Speed: Removing speed bumps in API Projects
 
Worldwide Software Architecture Summit'23 - BDD and why most of us do it wron...
Worldwide Software Architecture Summit'23 - BDD and why most of us do it wron...Worldwide Software Architecture Summit'23 - BDD and why most of us do it wron...
Worldwide Software Architecture Summit'23 - BDD and why most of us do it wron...
 
4Developers - Rozterki i decyzje.pdf
4Developers - Rozterki i decyzje.pdf4Developers - Rozterki i decyzje.pdf
4Developers - Rozterki i decyzje.pdf
 
4Developers - Sylius CRUD generation revisited.pdf
4Developers - Sylius CRUD generation revisited.pdf4Developers - Sylius CRUD generation revisited.pdf
4Developers - Sylius CRUD generation revisited.pdf
 
BoilingFrogs - Rozterki i decyzje. Czego się nauczyliśmy projektując API Syliusa
BoilingFrogs - Rozterki i decyzje. Czego się nauczyliśmy projektując API SyliusaBoilingFrogs - Rozterki i decyzje. Czego się nauczyliśmy projektując API Syliusa
BoilingFrogs - Rozterki i decyzje. Czego się nauczyliśmy projektując API Syliusa
 
What we've learned designing new Sylius API
What we've learned designing new Sylius APIWhat we've learned designing new Sylius API
What we've learned designing new Sylius API
 
How to optimize background processes.pdf
How to optimize background processes.pdfHow to optimize background processes.pdf
How to optimize background processes.pdf
 
SymfonyCon - Dilemmas and decisions..pdf
SymfonyCon - Dilemmas and decisions..pdfSymfonyCon - Dilemmas and decisions..pdf
SymfonyCon - Dilemmas and decisions..pdf
 
How to optimize background processes - when Sylius meets Blackfire
How to optimize background processes - when Sylius meets BlackfireHow to optimize background processes - when Sylius meets Blackfire
How to optimize background processes - when Sylius meets Blackfire
 
Sylius and Api Platform The story of integration
Sylius and Api Platform The story of integrationSylius and Api Platform The story of integration
Sylius and Api Platform The story of integration
 
Dutch php a short tale about state machine
Dutch php   a short tale about state machineDutch php   a short tale about state machine
Dutch php a short tale about state machine
 
A short tale about state machine
A short tale about state machineA short tale about state machine
A short tale about state machine
 
A short tale about state machine
A short tale about state machineA short tale about state machine
A short tale about state machine
 
BDD in practice based on an open source project
BDD in practice based on an open source projectBDD in practice based on an open source project
BDD in practice based on an open source project
 
Why do I love and hate php?
Why do I love and hate php?Why do I love and hate php?
Why do I love and hate php?
 

Kürzlich hochgeladen

%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
masabamasaba
 
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
masabamasaba
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
masabamasaba
 
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
VictoriaMetrics
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
masabamasaba
 

Kürzlich hochgeladen (20)

Architecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastArchitecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the past
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand
 
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
 
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
 
What Goes Wrong with Language Definitions and How to Improve the Situation
What Goes Wrong with Language Definitions and How to Improve the SituationWhat Goes Wrong with Language Definitions and How to Improve the Situation
What Goes Wrong with Language Definitions and How to Improve the Situation
 
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
 
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
 
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
 
Announcing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareAnnouncing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK Software
 
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
 
Artyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptxArtyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptx
 
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
 
WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...
WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...
WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...
 
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
 
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
 

Diversified application testing based on a Sylius project

  • 1. DIVERSIFIED APPLICATION TESTING BASED ON A SYLIUS PROJECT Łukasz Chruściel
  • 2. AGENDA • Sylius in a nutshell • Tests and their types • PHPSpec usage for unit testing • API testing with ApiTestCase • New approach to Behat • Summary
  • 4. WHAT IS SYLIUS? 330+ contributors 10k+ contributions 5 years of development Still in dev A few revolutions behind us 60+ packages Full stack BDD OSS Project E-commerce platform
  • 5. REVOLUTIONS? Fixtures system Shop Admin panel Repositories Factories Translations New approach of Behat User handling Products Checkout flow State machines
  • 6.
  • 7. HOW DID WE HANDLE IT?
  • 9. TYPES OFTESTS • Unit • Integration • Functional • Stress • Performance • Usability • Acceptance • Regression • API • GUI
  • 10. TYPES OFTESTS • Unit • Integration • Functional • Acceptance • API • GUI
  • 12. EASYTO UNDERSTAND final class TypicalCalculatorSpec extends ObjectBehavior { function it_is_initializable() { $this->shouldHaveType(Calculator::class); } function it_adds_two_numbers() { $this->add(2, 4)->shouldReturn(6); } } AcmeAppBundleCalculatorTypicalCalculator 25 ✔ is initializable 35 ✔ adds two numbers 1 specs 3 examples (3 passed) 11ms final class TypicalCalculator { public function add($a, $b) { return $a + $b; } }
  • 13. SOMETHING MORE SOPHISTICATED class AverageRatingCalculator implements ReviewableRatingCalculatorInterface { public function calculate(ReviewableInterface $reviewable) { $sum = 0; $reviewsNumber = 0; $reviews = $reviewable->getReviews(); foreach ($reviews as $review) { if (ReviewInterface::STATUS_ACCEPTED === $review->getStatus()) { ++$reviewsNumber; $sum += $review->getRating(); } } return 0 !== $reviewsNumber ? $sum / $reviewsNumber : 0; } }
  • 14. SOMETHING MORE SOPHISTICATED function it_calculates_average_price( ReviewableInterface $reviewable, ReviewInterface $review1, ReviewInterface $review2 ) { $reviewable->getReviews()->willReturn([$review1, $review2]); $review1 ->getStatus() ->willReturn(ReviewInterface::STATUS_ACCEPTED); $review2 ->getStatus() ->willReturn(ReviewInterface::STATUS_ACCEPTED); $review1->getRating()->willReturn(4); $review2->getRating()->willReturn(5); $this->calculate($reviewable)->shouldReturn(4.5); }
  • 15. SOMETHING MORE SOPHISTICATED function it_returns_zero_if_given_reviewable_object_has_no_reviews( ReviewableInterface $reviewable ){ $reviewable->getReviews()->willReturn([])); $this->calculate($reviewable)->shouldReturn(0); }
  • 16. SOMETHING MORE SOPHISTICATED function it_returns_zero_if_given_reviewable_object_has_reviews_but_none_of_them_is_accepted( ReviewableInterface $reviewable, ReviewInterface $review ) { $reviewable->getReviews()->willReturn([$review]); $review->getStatus()->willReturn(ReviewInterface::STATUS_NEW); $this->calculate($reviewable)->shouldReturn(0); }
  • 17. SOMETHING MORE SOPHISTICATED class AverageRatingCalculator implements ReviewableRatingCalculatorInterface { public function calculate(ReviewableInterface $reviewable) { $sum = 0; $reviewsNumber = 0; $reviews = $reviewable->getReviews(); foreach ($reviews as $review) { if (ReviewInterface::STATUS_ACCEPTED === $review->getStatus()) { ++$reviewsNumber; $sum += $review->getRating(); } } return 0 !== $reviewsNumber ? $sum / $reviewsNumber : 0; } }
  • 18. COLLABORATORS? namespace SyliusBundleOrderBundleNumberAssigner; final class OrderNumberAssigner implements OrderNumberAssignerInterface { private $numberGenerator; public function __construct(OrderNumberGeneratorInterface $numberGenerator) { $this->numberGenerator = $numberGenerator; } public function assignNumber(OrderInterface $order) { if (null !== $order->getNumber()) { return; } $order->setNumber($this->numberGenerator->generate($order)); } }
  • 19. COLLABORATORS? namespace specSyliusBundleOrderBundleNumberAssigner; final class OrderNumberAssignerSpec extends ObjectBehavior { function let(OrderNumberGeneratorInterface $numberGenerator) { $this->beConstructedWith($numberGenerator); } function it_assigns_number_to_order( OrderInterface $order, OrderNumberGeneratorInterface $numberGenerator ) { $order->getNumber()->willReturn(null); $numberGenerator->generate($order)->willReturn('00000007'); $order->setNumber('00000007')->shouldBeCalled(); $this->assignNumber($order); } function it_does_not_assign_number_to_order_with_number( OrderInterface $order, OrderNumberGeneratorInterface $numberGenerator ) { $order->getNumber()->willReturn('00000007'); $numberGenerator->generate($order)->shouldNotBeCalled(); $order->setNumber(Argument::any())->shouldNotBeCalled(); $this->assignNumber($order); } }
  • 20. WHAT DO WETEST WITH PHPSPEC? *Although we don’t spec repositories, nor forms. EVERYTHING*
  • 21. „BUT IT IS HARD/IMPOSSIBLE TO SPEC MY CODE!”
  • 24. JSON?
  • 25. WHAT DO WE HAVE? URL: Raw data to send: Expected response: POST /api/countries/ {"code": "BE"} { "id": 1, "code": "BE" }
  • 26. namespace SyliusTestController; class CountryApiTest extends JsonApiTestCase { public function testCreateCountryResponse() { $data = <<<EOT { "code": "BE" } EOT; $this->client->request('POST', '/api/countries/', [], [], [], $data); $response = $this->client->getResponse(); $this->assertResponse($response, 'country/create_response', Response::HTTP_CREATED); } } // test/Responses/Expected/country/create_response.json { "id": @integer@, "code": "BE" }
  • 27. XML?
  • 28. WHAT DO WE HAVE? URL: Expected response: GET /sitemap.xml <?xml version="1.0" encoding="UTF-8"?> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <url> <loc>http://sylius.org/products/mug-star-wars</loc> <lastmod>2015-10-10T00:00:00+02:00</lastmod> <changefreq>always</changefreq> <priority>0.5</priority> </url> <url> <loc>http://sylius.org/products/mug-lotr</loc> <lastmod>2015-10-04T00:00:00+02:00</lastmod> <changefreq>always</changefreq> <priority>0.5</priority> </url> <url> <loc>http://sylius.org/products/mug-breaking-bad</loc> <lastmod>2015-10-05T00:00:00+02:00</lastmod> <changefreq>always</changefreq> <priority>0.5</priority> </url> </urlset>
  • 29. // test/Responses/Expected/sitemap/show_sitemap.xml <?xml version="1.0" encoding="UTF-8"?> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <url> <loc>http://sylius.org/products/mug-star-wars</loc> <lastmod>@string@.isDateTime()</lastmod> <changefreq>always</changefreq> <priority>0.5</priority> </url> <url> <loc>http://sylius.org/products/mug-lotr</loc> <lastmod>@string@.isDateTime()</lastmod> <changefreq>always</changefreq> <priority>0.5</priority> </url> <url> <loc>http://sylius.org/products/mug-breaking-bad</loc> <lastmod>@string@.isDateTime()</lastmod> <changefreq>always</changefreq> <priority>0.5</priority> </url> </urlset> namespace SyliusTestController; class SitemapControllerApiTest extends XmlApiTestCase { public function testShowActionResponse() { $this->loadFixturesFromFile('resources/product.yml'); $this->client->request('GET', '/sitemap.xml'); $response = $this->client->getResponse(); $this->assertResponse($response, 'sitemap/show_sitemap'); } }
  • 30. HELPS IN DEBUGGING 1) SyliusTestsControllerOauthTokenApiTest::it_provides_an_access_token "3600" does not match "7200". @@ -1,7 +1,7 @@ { - "expires_in": 7200, + "expires_in": 3600, "token_type": "bearer", "scope": null, }
  • 33. WHAT WASTHAT? Uber Behat contexts 800+ lines of code Many ancestors Each Behat context got an `F` degree on scrutinizer
  • 34. WAS IT WRONG? /** * @Given /^I am on the page of ([^""(w)]*) "([^""]*)"$/ * @Given /^I go to the page of ([^""(w)]*) "([^""]*)"$/ */ public function iAmOnTheResourcePageByName($type, $name) { if ('country' === $type) { $this->iAmOnTheCountryPageByName($name); return; } $this->iAmOnTheResourcePage($type, 'name', $name); }
  • 35. WAS IT WRONG? IT WAS TERRIBLE!
  • 37. STYLE
  • 38. @promotions Feature: Checkout fixed discount promotions In order to handle product promotions As a store owner I want to apply promotion discounts during checkout Background: Given store has default configuration And the following countries exist: | name | | Germany | | Poland | And there are following taxonomies defined: | name | | Category | And taxonomy "Category" has following taxons: | Clothing > DebianT-Shirts | And the following products exist: | name | price | taxons | | Woody | 125 | DebianT-Shirts | And the following promotions exist: | name | description | | 3 items | Discount for orders with at least 3 items | And all products are assigned to the default channel And all promotions are assigned to the default channel And promotion "3 items" has following rules defined: | type | configuration | | Item count | Count: 3,Equal: true | And promotion "3 items" has following actions defined: | type | configuration | | Fixed discount | Amount: 15 | And I am logged in as user "klaus@example.com" Scenario: Fixed discount promotion is applied when the cart has the required amount Given I am on the store homepage When I add product "Woody" to cart, with quantity "3" Then I should be on the cart summary page And "Promotion total: -€40.00" should appear on the page And "Grand total: €295.00" should appear on the page @receiving_discount Feature: Receiving fixed discount on cart In order to pay proper amount while buying promoted goods As aVisitor I want to have promotions applied to my cart Background: Given the store operates on a single channel in "France" And the store has a product "PHPT-Shirt" priced at "€100.00" And the store has a product "PHP Mug" priced at "€6.00" @ui Scenario: Receiving fixed discount for my cart Given there is a promotion "Holiday promotion" And it gives "€10.00" discount to every order When I add product "PHPT-Shirt" to the cart Then my cart total should be "€90.00" And my discount should be "-€10.00"
  • 39. @receiving_discount Feature: Receiving percentage discount on shipping In order to pay decreased amount for shipping As a Customer I want to have shipping promotion applied to my cart Background: Given the store operates on a single channel in "France" And the store has "DHL" shipping method with "€10.00" fee And the store has a product "PHPT-Shirt" priced at "€100.00" And there is a promotion "Holiday promotion" And I am a logged in customer @ui Scenario: Receiving percentage discount on shipping Given the promotion gives "20%" discount on shipping to every order When I add product "PHPT-Shirt" to the cart And I proceed selecting "DHL" shipping method Then my cart total should be "€108.00" And my cart shipping total should be "€8.00"
  • 41. TRANSFORMERS <?php final class PromotionContext implements Context { /** * @Then promotion :promotion should still exist in the registry */ public function promotionShouldStillExistInTheRegistry(PromotionInterface $promotion) { Assert::notNull($this->promotionRepository->find($promotion->getId())); } } <?php final class PromotionContext implements Context { /** * @Transform :promotion */ public function getPromotionByName($promotionName) { return $this->promotionRepository->findOneBy(['name' => $promotionName]); } } @domain @ui
 Scenario: Being unable to delete a promotion that was applied to an order
 When I try to delete a "Christmas sale" promotion
 Then I should be notified that it is in use and cannot be deleted
 And promotion "Christmas sale" should still exist in the registry
  • 42. TAGS namespace SyliusBehatContextDomain; final class OrderContext implements Context { /** * @When I delete the order :order */ public function iDeleteTheOrder(OrderInterface $order) { $this->orderRepository->remove($order); } } namespace SyliusBehatContextUi; final class OrderContext implements Context { /** * @When I delete the order :order */ public function iDeleteTheOrder(OrderInterface $order) { $this->showPage->open(['id' => $order->getId()]); $this->showPage->deleteOrder(); } } @domain @ui Scenario: Deleted order should disappear from the registry When I delete the order "#00000022" Then I should be notified that it has been successfully deleted And this order should not exist in the registry
  • 43. RECOMENDATIONS Tags steps should be written in such a way that can be interpreted in many contexts Do not create a new object withTransformers Shared storage container to keep data between contexts Use ubiquitous language
  • 45. PAGE AS A SERVICE namespace SyliusBehatPageAdminAccount; class LoginPage implements LoginPageInterface { public function specifyPassword($password) { $this->getDocument()->fillField('Password', $password); } public function specifyUsername($username) { $this->getDocument()->fillField('Username', $username); } } namespace SyliusBehatContextUiAdmin; final class LoginContext implements Context { public function __construct(LoginPageInterface $loginPage) { $this->loginPage = $loginPage; } 
 /** @When I specify the username as :username */ public function iSpecifyTheUsername($username) { $this->loginPage->specifyUsername($username); } 
 /** @When I specify the password as :password */ public function iSpecifyThePasswordAs($password) { $this->loginPage->specifyPassword($password); } }
  • 46. CONTEXT AS A SERVICE default: suites: ui_administrator_login: contexts_as_services: - sylius.behat.context.hook.doctrine_orm - sylius.behat.context.transform.user - sylius.behat.context.setup.channel - sylius.behat.context.setup.admin_user - sylius.behat.context.setup.user - sylius.behat.context.ui.admin.login filters: tags: "@administrator_login && @ui" namespace SyliusBehatContextUiAdmin; final class LoginContext implements Context { public function __construct(LoginPageInterface $loginPage) { $this->loginPage = $loginPage; } /** * @Given I want to log in */ public function iWantToLogIn(){…} /** * @When I specify the username as :username */ public function iSpecifyTheUsername($username){…} /** * @When I specify the password as :password */ public function iSpecifyThePasswordAs($password){…} /** * @When I log in */ public function iLogIn(){…} }
  • 49. QUESTIONS? Łukasz Chruściel @lukaszchrusciel https://github.com/lchrusciel Worth to see: http://www.phpspec.net/en/stable/ http://docs.behat.org/en/v3.0/ https://github.com/Lakion/ApiTestCase https://github.com/Sylius/Sylius http://martinfowler.com/bliki/PageObject.html http://mink.behat.org/en/latest/ https://github.com/FriendsOfBehat