SlideShare a Scribd company logo
1 of 74
Download to read offline
Your code
are my tests
How to test legacy code
in it2PROFESSIONAL PHP SERVICES
ADVISORY
IN ORDER TO EXPLAIN CERTAIN SITUATIONS YOU MIGHT FACE IN YOUR
DEVELOPMENT CAREER, WE WILL BE DISCUSSING THE USAGE OF
PRIVATES AND PUBLIC EXPOSURE. IF THESE TOPICS OFFEND OR
UPSET YOU, WE WOULD LIKE TO ASK YOU TO LEAVE THIS ROOM NOW.
THE SPEAKER NOR THE ORGANISATION CANNOT BE HELD
ACCOUNTABLE FOR MENTAL DISTRESS OR ANY FORMS OF DAMAGE
YOU MIGHT ENDURE DURING OR AFTER THIS PRESENTATION. FOR
COMPLAINTS PLEASE INFORM ORGANISATION AT INFO@IN2IT.BE.
Michelangelo van Dam
PHP Consultant
Community Leader
President of PHPBenelux
Contributor to PHP projects
T @DragonBe | F DragonBe
https://www.flickr.com/photos/akrabat/8784318813
Using Social Media?
Tag it #mytests
http://www.flickr.com/photos/andyofne/4633356197
http://www.flickr.com/photos/andyofne/4633356197
Why bother with testing?
https://www.flickr.com/photos/vialbost/5533266530
Most common excuses
why developers don’t test
• no time
• no budget
• deliver tests after finish project
(never)
• devs don’t know how
https://www.flickr.com/photos/dasprid/8147986307
No excuses!!!
Crea%ve	
  Commons	
  -­‐	
  h.p://www.flickr.com/photos/akrabat/8421560178
Responsibility issue
• As a developer, it’s your job to
• write code & fixing bugs
• add documentation
• write & update unit tests
Pizza principleTopping:	
  your	
  tests
Box:	
  your	
  documenta%on
Dough:	
  your	
  code
Benefits of testing
• Direct feedback (test fails)
• Once a test is made, it will always be tested
• Easy to refactor existing code (protection)
• Easy to debug: write a test to see if a bug is
genuine
• Higher confidence and less uncertainty
Rule of thumb
“Whenever you are tempted to type something into a
print statement or a debugger expression, write it as
a test instead.”
— Source: Martin Fowler
Warming up
https://www.flickr.com/photos/bobjagendorf/8535316836
PHPUnit
• PHPUnit is a port of xUnit testing framework
• Created by “Sebastian Bergmann”
• Uses “assertions” to verify behaviour of “unit of code”
• Open source and hosted on GitHub
• See https://github.com/sebastianbergmann/phpunit
• Can be installed using:
• PEAR
• PHAR
• Composer
Approach for testing
• Instantiate a “unit-of-code”
• Assert expected result against actual result
• Provide a custom error message
Available assertions
• assertArrayHasKey()
• assertClassHasAttribute()
• assertClassHasStaticAttribute()
• assertContains()
• assertContainsOnly()
• assertContainsOnlyInstancesOf()
• assertCount()
• assertEmpty()
• assertEqualXMLStructure()
• assertEquals()
• assertFalse()
• assertFileEquals()
• assertFileExists()
• assertGreaterThan()
• assertGreaterThanOrEqual()
• assertInstanceOf()
• assertInternalType()
• assertJsonFileEqualsJsonFile()
• assertJsonStringEqualsJsonFile()
• assertJsonStringEqualsJsonString()
• assertLessThan()
• assertLessThanOrEqual()
• assertNull()
• assertObjectHasAttribute()
• assertRegExp()
• assertStringMatchesFormat()
• assertStringMatchesFormatFile()
• assertSame()
• assertSelectCount()
• assertSelectEquals()
• assertSelectRegExp()
• assertStringEndsWith()
• assertStringEqualsFile()
• assertStringStartsWith()
• assertTag()
• assertThat()
• assertTrue()
• assertXmlFileEqualsXmlFile()
• assertXmlStringEqualsXmlFile()
• assertXmlStringEqualsXmlString()
To	
  protect	
  and	
  to	
  serve
Data is tainted, ALWAYS
Hackers
BAD DATA
Web Services
Stupid users
Your code are my tests
Your code are my tests
OWASP top 10 exploits
https://www.owasp.org/index.php/Top_10_2013-Top_10
Filtering & Validation
Smallest unit of code
https://www.flickr.com/photos/toolstop/4546017269
Example class
<?php
/**
 * Example class
 */
class MyClass
{
    /** ... */
    public function doSomething($requiredParam, $optionalParam = null)
    {
        if (!filter_var(
            $requiredParam, FILTER_SANITIZE_STRING, FILTER_FLAG_ENCODE_HIGH
        )) {
            throw new InvalidArgumentException('Invalid argument provided');
        }
        if (null !== $optionalParam) {
            if (!filter_var(
                $optionalParam, FILTER_SANITIZE_STRING, FILTER_FLAG_ENCODE_HIGH
            )) {
                throw new InvalidArgumentException('Invalid argument provided');
            }
            $requiredParam .= ' - ' . $optionalParam;
        }
        return $requiredParam;
    }
}
Testing for good
   /** ... */
    public function testClassAcceptsValidRequiredArgument()
    {
        $expected = $argument = 'Testing PHP Class';
        $myClass = new MyClass;
        $result = $myClass->doSomething($argument);
        $this->assertSame($expected, $result, 
            'Expected result differs from actual result');
    }
   /** ... */    
    public function testClassAcceptsValidOptionalArgument()
    {
        $requiredArgument = 'Testing PHP Class';
        $optionalArgument = 'Is this not fun?!?';
        $expected = $requiredArgument . ' - ' . $optionalArgument;
        $myClass = new MyClass;
        $result = $myClass->doSomething($requiredArgument, $optionalArgument);
        $this->assertSame($expected, $result, 
            'Expected result differs from actual result');
    }
Testing for bad
    /**
     * @expectedException InvalidArgumentException
     */
    public function testExceptionIsThrownForInvalidRequiredArgument()
    {
        $expected = $argument = new StdClass;
        $myClass = new MyClass;
        $result = $myClass->doSomething($argument);
        $this->assertSame($expected, $result, 
            'Expected result differs from actual result');
    }
    
    /**
     * @expectedException InvalidArgumentException
     */
    public function testExceptionIsThrownForInvalidOptionalArgument()
    {
        $requiredArgument = 'Testing PHP Class';
        $optionalArgument = new StdClass;
        $myClass = new MyClass;
        $result = $myClass->doSomething($requiredArgument, $optionalArgument);
        $this->assertSame($expected, $result, 
            'Expected result differs from actual result');
    }
Example: testing payments
<?php	
  
namespace	
  MyappCommonPayment;	
  
	
  	
  
class	
  ProcessTest	
  extends	
  PHPUnit_Framework_TestCase	
  
{	
  
	
  	
  	
  	
  public	
  function	
  testPaymentIsProcessedCorrectly()	
  
	
  	
  	
  	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  $customer	
  =	
  new	
  Customer(/*	
  data	
  for	
  customer	
  */);	
  
	
  	
  	
  	
  	
  	
  	
  	
  $transaction	
  =	
  new	
  Transaction(/*	
  data	
  for	
  transaction	
  */);	
  
	
  	
  	
  	
  	
  	
  	
  	
  $process	
  =	
  new	
  Process('sale',	
  $customer,	
  $transaction);	
  
	
  	
  	
  	
  	
  	
  	
  	
  $process-­‐>pay();	
  
	
  	
  
	
  	
  	
  	
  	
  	
  	
  	
  $this-­‐>assertTrue($process-­‐>paymentApproved());	
  
	
  	
  	
  	
  	
  	
  	
  	
  $this-­‐>assertEquals('PAY-­‐17S8410768582940NKEE66EQ',	
  $process-­‐
>getPaymentId());	
  
	
  	
  	
  	
  }	
  
}
We don’t live in a fairy tale!
https://www.flickr.com/photos/bertknot/8175214909
Real code, real apps
github.com/Telaxus/EPESI
Running the project
Where are the TESTS?
Where are the TESTS?
Oh noes, no tests!
https://www.flickr.com/photos/mjhagen/2973212926
Let’s get started
https://www.flickr.com/photos/npobre/2601582256
How to get about it?
Setting up for testing
<phpunit colors="true" stopOnError="true" stopOnFailure="true">
<testsuites>
<testsuite name="EPESI admin tests">
<directory phpVersion="5.3.0">tests/admin</directory>
</testsuite>
<testsuite name="EPESI include tests">
<directory phpVersion="5.3.0">tests/include</directory>
</testsuite>
<testsuite name="EPESI modules testsuite">
<directory phpVersion="5.3.0">tests/modules</directory>
</testsuite>
</testsuites>
<php>
<const name="DEBUG_AUTOLOADS" value="1"/>
<const name="CID" value="1234567890123456789"/>
</php>
<logging>
<log type="coverage-html" target="build/coverage" charset="UTF-8"/>
<log type="coverage-clover" target="build/logs/clover.xml"/>
<log type="junit" target="build/logs/junit.xml"/>
</logging>
</phpunit>
ModuleManager
• not_loaded_modules
• loaded_modules
• modules
• modules_install
• modules_common
• root
• processing
• processed_modules
• include_install
• include_common
• include_main
• create_load_priority_array
• check_dependencies
• satisfy_dependencies
• get_module_dir_path
• get_module_file_name
• list_modules
• exists
• register
• unregister
• is_installed
• upgrade
• downgrade
• get_module_class_name
• install
• uninstall
• get_processed_modules
• get_load_priority_array
• new_instance
• get_instance
• create_data_dir
• remove_data_dir
• get_data_dir
• load_modules
• create_common_cache
• create_root
• check_access
• call_common_methods
• check_common_methods
• required_modules
• reset_cron
ModuleManager::module_in
stall
/**
 * Includes file with module installation class.
 *
 * Do not use directly.
 *
 * @param string $module_class_name module class name - underscore separated
 */
public static final function include_install($module_class_name) {
    if(isset(self::$modules_install[$module_class_name])) return true;
    $path = self::get_module_dir_path($module_class_name);
    $file = self::get_module_file_name($module_class_name);
    $full_path = 'modules/' . $path . '/' . $file . 'Install.php';
    if (!file_exists($full_path)) return false;
    ob_start();
    $ret = require_once($full_path);
    ob_end_clean();
    $x = $module_class_name.'Install';
    if(!(class_exists($x, false)) || 
!array_key_exists('ModuleInstall',class_parents($x)))
        trigger_error('Module '.$path.': Invalid install file',E_USER_ERROR);
    self::$modules_install[$module_class_name] = new $x($module_class_name);
    return true;
}
Testing first condition
<?php
require_once 'include.php';
class ModuleManagerTest extends PHPUnit_Framework_TestCase
{
    protected function tearDown()
    {
        ModuleManager::$modules_install = array ();
    }
    public function testReturnImmediatelyWhenModuleAlreadyLoaded()
    {
        $module = 'Foo_Bar';
        ModuleManager::$modules_install[$module] = 1;
        $result = ModuleManager::include_install($module);
        $this->assertTrue($result,
            'Expecting that an already installed module returns true');
        $this->assertCount(1, ModuleManager::$modules_install,
            'Expecting to find 1 module ready for installation');
    }
}
Run test
Check coverage
Test for second condition
public function testLoadingNonExistingModuleIsNotExecuted()
{
    $module = 'Foo_Bar';
    $result = ModuleManager::include_install($module);
    $this-
>assertFalse($result, 'Expecting failure for loading Foo_Bar');
    $this->assertEmpty(ModuleManager::$modules_install,
        'Expecting to find no modules ready for installation');
}
Run tests
Check coverage
Test for third condition
public function testNoInstallationOfModuleWithoutInstallationClass(
)
{
    $module = 'EssClient_IClient';
    $result = ModuleManager::include_install($module);
    $this-
>assertFalse($result, 'Expecting failure for loading Foo_Bar');
    $this->assertEmpty(ModuleManager::$modules_install,
        'Expecting to find no modules ready for installation');
}
Run tests
Check code coverage
Non-executable code
https://www.flickr.com/photos/dazjohnson/7720806824
Test for success
public function testIncludeClassFileForLoadingModule()
{
    $module = 'Base_About';
    $result = ModuleManager::include_install($module);
    $this->assertTrue($result, 'Expected module to be loaded');
    $this->assertCount(1, ModuleManager::$modules_install,
        'Expecting to find 1 module ready for installation');
}
Run tests
Check code coverage
Look at the global coverage
Bridging gaps
https://www.flickr.com/photos/hugo90/6980712643
Privates exposed
http://www.slashgear.com/former-tsa-agent-admits-we-knew-full-body-scanners-didnt-work-31315288/
Dependency
• __construct
• get_module_name
• get_version_min
• get_version_max
• is_satisfied_by
• requires
• requires_exact
• requires_at_least
• requires_range
A private constructor!
<?php
defined("_VALID_ACCESS") || die('Direct access forbidden');
/**
 * This class provides dependency requirements
 * @package epesi-base
 * @subpackage module 
 */
class Dependency {
    private $module_name;
    private $version_min;
    private $version_max;
    private $compare_max;
    private function __construct(
$module_name, $version_min, $version_max, $version_max_is_ok = true) {
        $this->module_name = $module_name;
        $this->version_min = $version_min;
        $this->version_max = $version_max;
        $this->compare_max = $version_max_is_ok ? '<=' : '<';
    }
    /** ... */
}
Don’t touch my junk!
https://www.flickr.com/photos/caseymultimedia/5412293730
House of Reflection
https://www.flickr.com/photos/tabor-roeder/8250770115
Let’s do this…
<?php
require_once 'include.php';
class DependencyTest extends PHPUnit_Framework_TestCase
{
    public function testConstructorSetsProperSettings()
    {
        require_once 'include/module_dependency.php';
        // We have a problem, the constructor is private!
    }
}
Let’s use the static
$params = array (
    'moduleName' => 'Foo_Bar',
    'minVersion' => 0,
    'maxVersion' => 1,
    'maxOk' => true,
);
// We use a static method for this test
$dependency = Dependency::requires_range(
    $params['moduleName'],
    $params['minVersion'],
    $params['maxVersion'],
    $params['maxOk']
);
// We use reflection to see if properties are set correctly
$reflectionClass = new ReflectionClass('Dependency');
Use the reflection to assert
// Let's retrieve the private properties
$moduleName = $reflectionClass->getProperty('module_name');
$moduleName->setAccessible(true);
$minVersion = $reflectionClass->getProperty('version_min');
$minVersion->setAccessible(true);
$maxVersion = $reflectionClass->getProperty('version_max');
$maxVersion->setAccessible(true);
$maxOk = $reflectionClass->getProperty('compare_max');
$maxOk->setAccessible(true);
// Let's assert
$this->assertEquals($params['moduleName'], $moduleName->getValue($dependency),
    'Expected value does not match the value set’);
$this->assertEquals($params['minVersion'], $minVersion->getValue($dependency),
    'Expected value does not match the value set’);
$this->assertEquals($params['maxVersion'], $maxVersion->getValue($dependency),
    'Expected value does not match the value set’);
$this->assertEquals('<=', $maxOk->getValue($dependency),
    'Expected value does not match the value set');
Run tests
Code Coverage
Yes, paradise exists
https://www.flickr.com/photos/rnugraha/2003147365
Unit testing is not
difficult!
Get started
PHP has all the tools
And there are more
roads to Rome
You’re one-stop fix
phpunit.de
Recommended reading
https://www.flickr.com/photos/lwr/13442542235
Contact us
in it2PROFESSIONAL PHP SERVICES
Michelangelo van Dam
michelangelo@in2it.be
www.in2it.be
PHP Consulting - Training - QA
cfp.phpbenelux.eu
Submit your proposals before
October 14, 2015
Thank you
Have a great conference
http://www.flickr.com/photos/drewm/3191872515

More Related Content

What's hot

Javascript Testing with Jasmine 101
Javascript Testing with Jasmine 101Javascript Testing with Jasmine 101
Javascript Testing with Jasmine 101Roy Yu
 
Jasmine - why JS tests don't smell fishy
Jasmine - why JS tests don't smell fishyJasmine - why JS tests don't smell fishy
Jasmine - why JS tests don't smell fishyIgor Napierala
 
Intro to testing Javascript with jasmine
Intro to testing Javascript with jasmineIntro to testing Javascript with jasmine
Intro to testing Javascript with jasmineTimothy Oxley
 
Good karma: UX Patterns and Unit Testing in Angular with Karma
Good karma: UX Patterns and Unit Testing in Angular with KarmaGood karma: UX Patterns and Unit Testing in Angular with Karma
Good karma: UX Patterns and Unit Testing in Angular with KarmaExoLeaders.com
 
Testing Javascript with Jasmine
Testing Javascript with JasmineTesting Javascript with Jasmine
Testing Javascript with JasmineTim Tyrrell
 
Caching and tuning fun for high scalability @ FrOSCon 2011
Caching and tuning fun for high scalability @ FrOSCon 2011Caching and tuning fun for high scalability @ FrOSCon 2011
Caching and tuning fun for high scalability @ FrOSCon 2011Wim Godden
 
Real World Dependency Injection - oscon13
Real World Dependency Injection - oscon13Real World Dependency Injection - oscon13
Real World Dependency Injection - oscon13Stephan Hochdörfer
 
The why and how of moving to PHP 5.4/5.5
The why and how of moving to PHP 5.4/5.5The why and how of moving to PHP 5.4/5.5
The why and how of moving to PHP 5.4/5.5Wim Godden
 
The why and how of moving to PHP 5.5/5.6
The why and how of moving to PHP 5.5/5.6The why and how of moving to PHP 5.5/5.6
The why and how of moving to PHP 5.5/5.6Wim Godden
 
Zend\Expressive - höher, schneller, weiter
Zend\Expressive - höher, schneller, weiterZend\Expressive - höher, schneller, weiter
Zend\Expressive - höher, schneller, weiterRalf Eggert
 
Workshop quality assurance for php projects - phpdublin
Workshop quality assurance for php projects - phpdublinWorkshop quality assurance for php projects - phpdublin
Workshop quality assurance for php projects - phpdublinMichelangelo van Dam
 
Compatibility Detector Tool of Chrome extensions
Compatibility Detector Tool of Chrome extensionsCompatibility Detector Tool of Chrome extensions
Compatibility Detector Tool of Chrome extensionsKai Cui
 
Fighting Fear-Driven-Development With PHPUnit
Fighting Fear-Driven-Development With PHPUnitFighting Fear-Driven-Development With PHPUnit
Fighting Fear-Driven-Development With PHPUnitJames Fuller
 
The Screenplay Pattern: Better Interactions for Better Automation
The Screenplay Pattern: Better Interactions for Better AutomationThe Screenplay Pattern: Better Interactions for Better Automation
The Screenplay Pattern: Better Interactions for Better AutomationApplitools
 

What's hot (20)

Night Watch with QA
Night Watch with QANight Watch with QA
Night Watch with QA
 
Jasmine BDD for Javascript
Jasmine BDD for JavascriptJasmine BDD for Javascript
Jasmine BDD for Javascript
 
My java file
My java fileMy java file
My java file
 
Javascript Testing with Jasmine 101
Javascript Testing with Jasmine 101Javascript Testing with Jasmine 101
Javascript Testing with Jasmine 101
 
Jasmine - why JS tests don't smell fishy
Jasmine - why JS tests don't smell fishyJasmine - why JS tests don't smell fishy
Jasmine - why JS tests don't smell fishy
 
Intro to testing Javascript with jasmine
Intro to testing Javascript with jasmineIntro to testing Javascript with jasmine
Intro to testing Javascript with jasmine
 
Good karma: UX Patterns and Unit Testing in Angular with Karma
Good karma: UX Patterns and Unit Testing in Angular with KarmaGood karma: UX Patterns and Unit Testing in Angular with Karma
Good karma: UX Patterns and Unit Testing in Angular with Karma
 
Testing Javascript with Jasmine
Testing Javascript with JasmineTesting Javascript with Jasmine
Testing Javascript with Jasmine
 
Caching and tuning fun for high scalability @ FrOSCon 2011
Caching and tuning fun for high scalability @ FrOSCon 2011Caching and tuning fun for high scalability @ FrOSCon 2011
Caching and tuning fun for high scalability @ FrOSCon 2011
 
Vuejs testing
Vuejs testingVuejs testing
Vuejs testing
 
How Testing Changed My Life
How Testing Changed My LifeHow Testing Changed My Life
How Testing Changed My Life
 
Real World Dependency Injection - oscon13
Real World Dependency Injection - oscon13Real World Dependency Injection - oscon13
Real World Dependency Injection - oscon13
 
The why and how of moving to PHP 5.4/5.5
The why and how of moving to PHP 5.4/5.5The why and how of moving to PHP 5.4/5.5
The why and how of moving to PHP 5.4/5.5
 
The why and how of moving to PHP 5.5/5.6
The why and how of moving to PHP 5.5/5.6The why and how of moving to PHP 5.5/5.6
The why and how of moving to PHP 5.5/5.6
 
Zend\Expressive - höher, schneller, weiter
Zend\Expressive - höher, schneller, weiterZend\Expressive - höher, schneller, weiter
Zend\Expressive - höher, schneller, weiter
 
Workshop quality assurance for php projects - phpdublin
Workshop quality assurance for php projects - phpdublinWorkshop quality assurance for php projects - phpdublin
Workshop quality assurance for php projects - phpdublin
 
Getting Testy With Perl6
Getting Testy With Perl6Getting Testy With Perl6
Getting Testy With Perl6
 
Compatibility Detector Tool of Chrome extensions
Compatibility Detector Tool of Chrome extensionsCompatibility Detector Tool of Chrome extensions
Compatibility Detector Tool of Chrome extensions
 
Fighting Fear-Driven-Development With PHPUnit
Fighting Fear-Driven-Development With PHPUnitFighting Fear-Driven-Development With PHPUnit
Fighting Fear-Driven-Development With PHPUnit
 
The Screenplay Pattern: Better Interactions for Better Automation
The Screenplay Pattern: Better Interactions for Better AutomationThe Screenplay Pattern: Better Interactions for Better Automation
The Screenplay Pattern: Better Interactions for Better Automation
 

Viewers also liked

Diving into HHVM Extensions (PHPNW Conference 2015)
Diving into HHVM Extensions (PHPNW Conference 2015)Diving into HHVM Extensions (PHPNW Conference 2015)
Diving into HHVM Extensions (PHPNW Conference 2015)James Titcumb
 
Composer the right way - SunshinePHP
Composer the right way - SunshinePHPComposer the right way - SunshinePHP
Composer the right way - SunshinePHPRafael Dohms
 
My app is secure... I think
My app is secure... I thinkMy app is secure... I think
My app is secure... I thinkWim Godden
 
Are you a good scout? - PHPNW15 Unconf
Are you a good scout? - PHPNW15 UnconfAre you a good scout? - PHPNW15 Unconf
Are you a good scout? - PHPNW15 Unconfphpboyscout
 
Secure Form Processing and Protection - Sunshine PHP 2015
Secure Form Processing and Protection - Sunshine PHP 2015Secure Form Processing and Protection - Sunshine PHP 2015
Secure Form Processing and Protection - Sunshine PHP 2015Joe Ferguson
 
Your Inner Sysadmin - Tutorial (SunshinePHP 2015)
Your Inner Sysadmin - Tutorial (SunshinePHP 2015)Your Inner Sysadmin - Tutorial (SunshinePHP 2015)
Your Inner Sysadmin - Tutorial (SunshinePHP 2015)Chris Tankersley
 
Building Your API for Longevity
Building Your API for LongevityBuilding Your API for Longevity
Building Your API for LongevityMuleSoft
 
Zephir - A Wind of Change for writing PHP extensions
Zephir - A Wind of Change for writing PHP extensionsZephir - A Wind of Change for writing PHP extensions
Zephir - A Wind of Change for writing PHP extensionsMark Baker
 
Introduction to Continuous Integration with Jenkins
Introduction to Continuous Integration with JenkinsIntroduction to Continuous Integration with Jenkins
Introduction to Continuous Integration with JenkinsEric Hogue
 
Driving Design through Examples
Driving Design through ExamplesDriving Design through Examples
Driving Design through ExamplesCiaranMcNulty
 
Hexagonal architecture message-oriented software design
Hexagonal architecture   message-oriented software designHexagonal architecture   message-oriented software design
Hexagonal architecture message-oriented software designMatthias Noback
 
Consequences of an Insightful Algorithm
Consequences of an Insightful AlgorithmConsequences of an Insightful Algorithm
Consequences of an Insightful AlgorithmCarina C. Zona
 

Viewers also liked (15)

Diving into HHVM Extensions (PHPNW Conference 2015)
Diving into HHVM Extensions (PHPNW Conference 2015)Diving into HHVM Extensions (PHPNW Conference 2015)
Diving into HHVM Extensions (PHPNW Conference 2015)
 
Composer the right way - SunshinePHP
Composer the right way - SunshinePHPComposer the right way - SunshinePHP
Composer the right way - SunshinePHP
 
My app is secure... I think
My app is secure... I thinkMy app is secure... I think
My app is secure... I think
 
Are you a good scout? - PHPNW15 Unconf
Are you a good scout? - PHPNW15 UnconfAre you a good scout? - PHPNW15 Unconf
Are you a good scout? - PHPNW15 Unconf
 
TDD: Team-Driven Development
TDD: Team-Driven DevelopmentTDD: Team-Driven Development
TDD: Team-Driven Development
 
Secure Form Processing and Protection - Sunshine PHP 2015
Secure Form Processing and Protection - Sunshine PHP 2015Secure Form Processing and Protection - Sunshine PHP 2015
Secure Form Processing and Protection - Sunshine PHP 2015
 
Your Inner Sysadmin - Tutorial (SunshinePHP 2015)
Your Inner Sysadmin - Tutorial (SunshinePHP 2015)Your Inner Sysadmin - Tutorial (SunshinePHP 2015)
Your Inner Sysadmin - Tutorial (SunshinePHP 2015)
 
Building Your API for Longevity
Building Your API for LongevityBuilding Your API for Longevity
Building Your API for Longevity
 
TDD with PhpSpec
TDD with PhpSpecTDD with PhpSpec
TDD with PhpSpec
 
Zephir - A Wind of Change for writing PHP extensions
Zephir - A Wind of Change for writing PHP extensionsZephir - A Wind of Change for writing PHP extensions
Zephir - A Wind of Change for writing PHP extensions
 
Dockerize All The Things
Dockerize All The ThingsDockerize All The Things
Dockerize All The Things
 
Introduction to Continuous Integration with Jenkins
Introduction to Continuous Integration with JenkinsIntroduction to Continuous Integration with Jenkins
Introduction to Continuous Integration with Jenkins
 
Driving Design through Examples
Driving Design through ExamplesDriving Design through Examples
Driving Design through Examples
 
Hexagonal architecture message-oriented software design
Hexagonal architecture   message-oriented software designHexagonal architecture   message-oriented software design
Hexagonal architecture message-oriented software design
 
Consequences of an Insightful Algorithm
Consequences of an Insightful AlgorithmConsequences of an Insightful Algorithm
Consequences of an Insightful Algorithm
 

Similar to Your code are my tests

PHPUnit Automated Unit Testing Framework
PHPUnit Automated Unit Testing FrameworkPHPUnit Automated Unit Testing Framework
PHPUnit Automated Unit Testing FrameworkDave Ross
 
High Stakes Continuous Delivery in the Real World #OpenWest
High Stakes Continuous Delivery in the Real World #OpenWestHigh Stakes Continuous Delivery in the Real World #OpenWest
High Stakes Continuous Delivery in the Real World #OpenWestJoshua Warren
 
PHPUnit with CakePHP and Yii
PHPUnit with CakePHP and YiiPHPUnit with CakePHP and Yii
PHPUnit with CakePHP and Yiimadhavi Ghadge
 
My app is secure... I think
My app is secure... I thinkMy app is secure... I think
My app is secure... I thinkWim Godden
 
Diagnosing WordPress: What to do when things go wrong
Diagnosing WordPress: What to do when things go wrongDiagnosing WordPress: What to do when things go wrong
Diagnosing WordPress: What to do when things go wrongWordCamp Sydney
 
Questioning the status quo
Questioning the status quoQuestioning the status quo
Questioning the status quoIvano Pagano
 
Test & Tea : ITSEC testing, manual vs automated
Test & Tea : ITSEC testing, manual vs automatedTest & Tea : ITSEC testing, manual vs automated
Test & Tea : ITSEC testing, manual vs automatedZoltan Balazs
 
TDD Painkillers
TDD PainkillersTDD Painkillers
TDD PainkillersBen Hall
 
Joomla! Day Chicago 2011 Presentation - Steven Pignataro
Joomla! Day Chicago 2011 Presentation - Steven PignataroJoomla! Day Chicago 2011 Presentation - Steven Pignataro
Joomla! Day Chicago 2011 Presentation - Steven PignataroSteven Pignataro
 
Using Puppet - Real World Configuration Management
Using Puppet - Real World Configuration ManagementUsing Puppet - Real World Configuration Management
Using Puppet - Real World Configuration ManagementJames Turnbull
 
Effizientere WordPress-Plugin-Entwicklung mit Softwaretests
Effizientere WordPress-Plugin-Entwicklung mit SoftwaretestsEffizientere WordPress-Plugin-Entwicklung mit Softwaretests
Effizientere WordPress-Plugin-Entwicklung mit SoftwaretestsDECK36
 
Leveraging Continuous Integration For Fun And Profit!
Leveraging Continuous Integration For Fun And Profit!Leveraging Continuous Integration For Fun And Profit!
Leveraging Continuous Integration For Fun And Profit!Jess Chadwick
 
Lecture2_IntroductionToPHP_Spring2023.pdf
Lecture2_IntroductionToPHP_Spring2023.pdfLecture2_IntroductionToPHP_Spring2023.pdf
Lecture2_IntroductionToPHP_Spring2023.pdfShaimaaMohamedGalal
 
WordPress Plugin Unit Tests (FR - WordCamp Paris 2015)
WordPress Plugin Unit Tests (FR - WordCamp Paris 2015)WordPress Plugin Unit Tests (FR - WordCamp Paris 2015)
WordPress Plugin Unit Tests (FR - WordCamp Paris 2015)Ozh
 
Nagios Conference 2014 - Rodrigo Faria - Developing your Plugin
Nagios Conference 2014 - Rodrigo Faria - Developing your PluginNagios Conference 2014 - Rodrigo Faria - Developing your Plugin
Nagios Conference 2014 - Rodrigo Faria - Developing your PluginNagios
 
How to Implement Token Authentication Using the Django REST Framework
How to Implement Token Authentication Using the Django REST FrameworkHow to Implement Token Authentication Using the Django REST Framework
How to Implement Token Authentication Using the Django REST FrameworkKaty Slemon
 

Similar to Your code are my tests (20)

PHPUnit Automated Unit Testing Framework
PHPUnit Automated Unit Testing FrameworkPHPUnit Automated Unit Testing Framework
PHPUnit Automated Unit Testing Framework
 
High Stakes Continuous Delivery in the Real World #OpenWest
High Stakes Continuous Delivery in the Real World #OpenWestHigh Stakes Continuous Delivery in the Real World #OpenWest
High Stakes Continuous Delivery in the Real World #OpenWest
 
PHPUnit with CakePHP and Yii
PHPUnit with CakePHP and YiiPHPUnit with CakePHP and Yii
PHPUnit with CakePHP and Yii
 
Revoke-Obfuscation
Revoke-ObfuscationRevoke-Obfuscation
Revoke-Obfuscation
 
My app is secure... I think
My app is secure... I thinkMy app is secure... I think
My app is secure... I think
 
Create, test, secure, repeat
Create, test, secure, repeatCreate, test, secure, repeat
Create, test, secure, repeat
 
Diagnosing WordPress: What to do when things go wrong
Diagnosing WordPress: What to do when things go wrongDiagnosing WordPress: What to do when things go wrong
Diagnosing WordPress: What to do when things go wrong
 
PHP Security
PHP SecurityPHP Security
PHP Security
 
Becoming A Php Ninja
Becoming A Php NinjaBecoming A Php Ninja
Becoming A Php Ninja
 
Questioning the status quo
Questioning the status quoQuestioning the status quo
Questioning the status quo
 
Test & Tea : ITSEC testing, manual vs automated
Test & Tea : ITSEC testing, manual vs automatedTest & Tea : ITSEC testing, manual vs automated
Test & Tea : ITSEC testing, manual vs automated
 
TDD Painkillers
TDD PainkillersTDD Painkillers
TDD Painkillers
 
Joomla! Day Chicago 2011 Presentation - Steven Pignataro
Joomla! Day Chicago 2011 Presentation - Steven PignataroJoomla! Day Chicago 2011 Presentation - Steven Pignataro
Joomla! Day Chicago 2011 Presentation - Steven Pignataro
 
Using Puppet - Real World Configuration Management
Using Puppet - Real World Configuration ManagementUsing Puppet - Real World Configuration Management
Using Puppet - Real World Configuration Management
 
Effizientere WordPress-Plugin-Entwicklung mit Softwaretests
Effizientere WordPress-Plugin-Entwicklung mit SoftwaretestsEffizientere WordPress-Plugin-Entwicklung mit Softwaretests
Effizientere WordPress-Plugin-Entwicklung mit Softwaretests
 
Leveraging Continuous Integration For Fun And Profit!
Leveraging Continuous Integration For Fun And Profit!Leveraging Continuous Integration For Fun And Profit!
Leveraging Continuous Integration For Fun And Profit!
 
Lecture2_IntroductionToPHP_Spring2023.pdf
Lecture2_IntroductionToPHP_Spring2023.pdfLecture2_IntroductionToPHP_Spring2023.pdf
Lecture2_IntroductionToPHP_Spring2023.pdf
 
WordPress Plugin Unit Tests (FR - WordCamp Paris 2015)
WordPress Plugin Unit Tests (FR - WordCamp Paris 2015)WordPress Plugin Unit Tests (FR - WordCamp Paris 2015)
WordPress Plugin Unit Tests (FR - WordCamp Paris 2015)
 
Nagios Conference 2014 - Rodrigo Faria - Developing your Plugin
Nagios Conference 2014 - Rodrigo Faria - Developing your PluginNagios Conference 2014 - Rodrigo Faria - Developing your Plugin
Nagios Conference 2014 - Rodrigo Faria - Developing your Plugin
 
How to Implement Token Authentication Using the Django REST Framework
How to Implement Token Authentication Using the Django REST FrameworkHow to Implement Token Authentication Using the Django REST Framework
How to Implement Token Authentication Using the Django REST Framework
 

More from Michelangelo van Dam

GDPR Art. 25 - Privacy by design and default
GDPR Art. 25 - Privacy by design and defaultGDPR Art. 25 - Privacy by design and default
GDPR Art. 25 - Privacy by design and defaultMichelangelo van Dam
 
Moving from app services to azure functions
Moving from app services to azure functionsMoving from app services to azure functions
Moving from app services to azure functionsMichelangelo van Dam
 
General Data Protection Regulation, a developer's story
General Data Protection Regulation, a developer's storyGeneral Data Protection Regulation, a developer's story
General Data Protection Regulation, a developer's storyMichelangelo van Dam
 
Leveraging a distributed architecture to your advantage
Leveraging a distributed architecture to your advantageLeveraging a distributed architecture to your advantage
Leveraging a distributed architecture to your advantageMichelangelo van Dam
 
Open source for a successful business
Open source for a successful businessOpen source for a successful business
Open source for a successful businessMichelangelo van Dam
 
Decouple your framework now, thank me later
Decouple your framework now, thank me laterDecouple your framework now, thank me later
Decouple your framework now, thank me laterMichelangelo van Dam
 
Deploy to azure in less then 15 minutes
Deploy to azure in less then 15 minutesDeploy to azure in less then 15 minutes
Deploy to azure in less then 15 minutesMichelangelo van Dam
 
Azure and OSS, a match made in heaven
Azure and OSS, a match made in heavenAzure and OSS, a match made in heaven
Azure and OSS, a match made in heavenMichelangelo van Dam
 
Zf2 how arrays will save your project
Zf2   how arrays will save your projectZf2   how arrays will save your project
Zf2 how arrays will save your projectMichelangelo van Dam
 
PHPUnit Episode iv.iii: Return of the tests
PHPUnit Episode iv.iii: Return of the testsPHPUnit Episode iv.iii: Return of the tests
PHPUnit Episode iv.iii: Return of the testsMichelangelo van Dam
 
Easily extend your existing php app with an api
Easily extend your existing php app with an apiEasily extend your existing php app with an api
Easily extend your existing php app with an apiMichelangelo van Dam
 

More from Michelangelo van Dam (20)

GDPR Art. 25 - Privacy by design and default
GDPR Art. 25 - Privacy by design and defaultGDPR Art. 25 - Privacy by design and default
GDPR Art. 25 - Privacy by design and default
 
Moving from app services to azure functions
Moving from app services to azure functionsMoving from app services to azure functions
Moving from app services to azure functions
 
Privacy by design
Privacy by designPrivacy by design
Privacy by design
 
DevOps or DevSecOps
DevOps or DevSecOpsDevOps or DevSecOps
DevOps or DevSecOps
 
Privacy by design
Privacy by designPrivacy by design
Privacy by design
 
Continuous deployment 2.0
Continuous deployment 2.0Continuous deployment 2.0
Continuous deployment 2.0
 
Let your tests drive your code
Let your tests drive your codeLet your tests drive your code
Let your tests drive your code
 
General Data Protection Regulation, a developer's story
General Data Protection Regulation, a developer's storyGeneral Data Protection Regulation, a developer's story
General Data Protection Regulation, a developer's story
 
Leveraging a distributed architecture to your advantage
Leveraging a distributed architecture to your advantageLeveraging a distributed architecture to your advantage
Leveraging a distributed architecture to your advantage
 
The road to php 7.1
The road to php 7.1The road to php 7.1
The road to php 7.1
 
Open source for a successful business
Open source for a successful businessOpen source for a successful business
Open source for a successful business
 
Decouple your framework now, thank me later
Decouple your framework now, thank me laterDecouple your framework now, thank me later
Decouple your framework now, thank me later
 
Deploy to azure in less then 15 minutes
Deploy to azure in less then 15 minutesDeploy to azure in less then 15 minutes
Deploy to azure in less then 15 minutes
 
Azure and OSS, a match made in heaven
Azure and OSS, a match made in heavenAzure and OSS, a match made in heaven
Azure and OSS, a match made in heaven
 
Getting hands dirty with php7
Getting hands dirty with php7Getting hands dirty with php7
Getting hands dirty with php7
 
Zf2 how arrays will save your project
Zf2   how arrays will save your projectZf2   how arrays will save your project
Zf2 how arrays will save your project
 
The Continuous PHP Pipeline
The Continuous PHP PipelineThe Continuous PHP Pipeline
The Continuous PHP Pipeline
 
PHPUnit Episode iv.iii: Return of the tests
PHPUnit Episode iv.iii: Return of the testsPHPUnit Episode iv.iii: Return of the tests
PHPUnit Episode iv.iii: Return of the tests
 
Easily extend your existing php app with an api
Easily extend your existing php app with an apiEasily extend your existing php app with an api
Easily extend your existing php app with an api
 
200K+ reasons security is a must
200K+ reasons security is a must200K+ reasons security is a must
200K+ reasons security is a must
 

Recently uploaded

Renewable Energy & Entrepreneurship Workshop_21Feb2024.pdf
Renewable Energy & Entrepreneurship Workshop_21Feb2024.pdfRenewable Energy & Entrepreneurship Workshop_21Feb2024.pdf
Renewable Energy & Entrepreneurship Workshop_21Feb2024.pdfodunowoeminence2019
 
Landsman converter for power factor improvement
Landsman converter for power factor improvementLandsman converter for power factor improvement
Landsman converter for power factor improvementVijayMuni2
 
Quasi-Stochastic Approximation: Algorithm Design Principles with Applications...
Quasi-Stochastic Approximation: Algorithm Design Principles with Applications...Quasi-Stochastic Approximation: Algorithm Design Principles with Applications...
Quasi-Stochastic Approximation: Algorithm Design Principles with Applications...Sean Meyn
 
sdfsadopkjpiosufoiasdoifjasldkjfl a asldkjflaskdjflkjsdsdf
sdfsadopkjpiosufoiasdoifjasldkjfl a asldkjflaskdjflkjsdsdfsdfsadopkjpiosufoiasdoifjasldkjfl a asldkjflaskdjflkjsdsdf
sdfsadopkjpiosufoiasdoifjasldkjfl a asldkjflaskdjflkjsdsdfJulia Kaye
 
Best-NO1 Best Rohani Amil In Lahore Kala Ilam In Lahore Kala Jadu Amil In Lah...
Best-NO1 Best Rohani Amil In Lahore Kala Ilam In Lahore Kala Jadu Amil In Lah...Best-NO1 Best Rohani Amil In Lahore Kala Ilam In Lahore Kala Jadu Amil In Lah...
Best-NO1 Best Rohani Amil In Lahore Kala Ilam In Lahore Kala Jadu Amil In Lah...Amil baba
 
Multicomponent Spiral Wound Membrane Separation Model.pdf
Multicomponent Spiral Wound Membrane Separation Model.pdfMulticomponent Spiral Wound Membrane Separation Model.pdf
Multicomponent Spiral Wound Membrane Separation Model.pdfGiovanaGhasary1
 
Test of Significance of Large Samples for Mean = µ.pptx
Test of Significance of Large Samples for Mean = µ.pptxTest of Significance of Large Samples for Mean = µ.pptx
Test of Significance of Large Samples for Mean = µ.pptxHome
 
SUMMER TRAINING REPORT ON BUILDING CONSTRUCTION.docx
SUMMER TRAINING REPORT ON BUILDING CONSTRUCTION.docxSUMMER TRAINING REPORT ON BUILDING CONSTRUCTION.docx
SUMMER TRAINING REPORT ON BUILDING CONSTRUCTION.docxNaveenVerma126
 
A Seminar on Electric Vehicle Software Simulation
A Seminar on Electric Vehicle Software SimulationA Seminar on Electric Vehicle Software Simulation
A Seminar on Electric Vehicle Software SimulationMohsinKhanA
 
Design of Clutches and Brakes in Design of Machine Elements.pptx
Design of Clutches and Brakes in Design of Machine Elements.pptxDesign of Clutches and Brakes in Design of Machine Elements.pptx
Design of Clutches and Brakes in Design of Machine Elements.pptxYogeshKumarKJMIT
 
nvidia AI-gtc 2024 partial slide deck.pptx
nvidia AI-gtc 2024 partial slide deck.pptxnvidia AI-gtc 2024 partial slide deck.pptx
nvidia AI-gtc 2024 partial slide deck.pptxjasonsedano2
 
ChatGPT-and-Generative-AI-Landscape Working of generative ai search
ChatGPT-and-Generative-AI-Landscape Working of generative ai searchChatGPT-and-Generative-AI-Landscape Working of generative ai search
ChatGPT-and-Generative-AI-Landscape Working of generative ai searchrohitcse52
 
Summer training report on BUILDING CONSTRUCTION for DIPLOMA Students.pdf
Summer training report on BUILDING CONSTRUCTION for DIPLOMA Students.pdfSummer training report on BUILDING CONSTRUCTION for DIPLOMA Students.pdf
Summer training report on BUILDING CONSTRUCTION for DIPLOMA Students.pdfNaveenVerma126
 
Phase noise transfer functions.pptx
Phase noise transfer      functions.pptxPhase noise transfer      functions.pptx
Phase noise transfer functions.pptxSaiGouthamSunkara
 
SATELITE COMMUNICATION UNIT 1 CEC352 REGULATION 2021 PPT BASICS OF SATELITE ....
SATELITE COMMUNICATION UNIT 1 CEC352 REGULATION 2021 PPT BASICS OF SATELITE ....SATELITE COMMUNICATION UNIT 1 CEC352 REGULATION 2021 PPT BASICS OF SATELITE ....
SATELITE COMMUNICATION UNIT 1 CEC352 REGULATION 2021 PPT BASICS OF SATELITE ....santhyamuthu1
 
me3493 manufacturing technology unit 1 Part A
me3493 manufacturing technology unit 1 Part Ame3493 manufacturing technology unit 1 Part A
me3493 manufacturing technology unit 1 Part Akarthi keyan
 

Recently uploaded (20)

Lecture 4 .pdf
Lecture 4                              .pdfLecture 4                              .pdf
Lecture 4 .pdf
 
Renewable Energy & Entrepreneurship Workshop_21Feb2024.pdf
Renewable Energy & Entrepreneurship Workshop_21Feb2024.pdfRenewable Energy & Entrepreneurship Workshop_21Feb2024.pdf
Renewable Energy & Entrepreneurship Workshop_21Feb2024.pdf
 
Landsman converter for power factor improvement
Landsman converter for power factor improvementLandsman converter for power factor improvement
Landsman converter for power factor improvement
 
Présentation IIRB 2024 Marine Cordonnier.pdf
Présentation IIRB 2024 Marine Cordonnier.pdfPrésentation IIRB 2024 Marine Cordonnier.pdf
Présentation IIRB 2024 Marine Cordonnier.pdf
 
Quasi-Stochastic Approximation: Algorithm Design Principles with Applications...
Quasi-Stochastic Approximation: Algorithm Design Principles with Applications...Quasi-Stochastic Approximation: Algorithm Design Principles with Applications...
Quasi-Stochastic Approximation: Algorithm Design Principles with Applications...
 
sdfsadopkjpiosufoiasdoifjasldkjfl a asldkjflaskdjflkjsdsdf
sdfsadopkjpiosufoiasdoifjasldkjfl a asldkjflaskdjflkjsdsdfsdfsadopkjpiosufoiasdoifjasldkjfl a asldkjflaskdjflkjsdsdf
sdfsadopkjpiosufoiasdoifjasldkjfl a asldkjflaskdjflkjsdsdf
 
Best-NO1 Best Rohani Amil In Lahore Kala Ilam In Lahore Kala Jadu Amil In Lah...
Best-NO1 Best Rohani Amil In Lahore Kala Ilam In Lahore Kala Jadu Amil In Lah...Best-NO1 Best Rohani Amil In Lahore Kala Ilam In Lahore Kala Jadu Amil In Lah...
Best-NO1 Best Rohani Amil In Lahore Kala Ilam In Lahore Kala Jadu Amil In Lah...
 
Multicomponent Spiral Wound Membrane Separation Model.pdf
Multicomponent Spiral Wound Membrane Separation Model.pdfMulticomponent Spiral Wound Membrane Separation Model.pdf
Multicomponent Spiral Wound Membrane Separation Model.pdf
 
Test of Significance of Large Samples for Mean = µ.pptx
Test of Significance of Large Samples for Mean = µ.pptxTest of Significance of Large Samples for Mean = µ.pptx
Test of Significance of Large Samples for Mean = µ.pptx
 
SUMMER TRAINING REPORT ON BUILDING CONSTRUCTION.docx
SUMMER TRAINING REPORT ON BUILDING CONSTRUCTION.docxSUMMER TRAINING REPORT ON BUILDING CONSTRUCTION.docx
SUMMER TRAINING REPORT ON BUILDING CONSTRUCTION.docx
 
A Seminar on Electric Vehicle Software Simulation
A Seminar on Electric Vehicle Software SimulationA Seminar on Electric Vehicle Software Simulation
A Seminar on Electric Vehicle Software Simulation
 
Design of Clutches and Brakes in Design of Machine Elements.pptx
Design of Clutches and Brakes in Design of Machine Elements.pptxDesign of Clutches and Brakes in Design of Machine Elements.pptx
Design of Clutches and Brakes in Design of Machine Elements.pptx
 
nvidia AI-gtc 2024 partial slide deck.pptx
nvidia AI-gtc 2024 partial slide deck.pptxnvidia AI-gtc 2024 partial slide deck.pptx
nvidia AI-gtc 2024 partial slide deck.pptx
 
Lecture 2 .pptx
Lecture 2                            .pptxLecture 2                            .pptx
Lecture 2 .pptx
 
ChatGPT-and-Generative-AI-Landscape Working of generative ai search
ChatGPT-and-Generative-AI-Landscape Working of generative ai searchChatGPT-and-Generative-AI-Landscape Working of generative ai search
ChatGPT-and-Generative-AI-Landscape Working of generative ai search
 
Summer training report on BUILDING CONSTRUCTION for DIPLOMA Students.pdf
Summer training report on BUILDING CONSTRUCTION for DIPLOMA Students.pdfSummer training report on BUILDING CONSTRUCTION for DIPLOMA Students.pdf
Summer training report on BUILDING CONSTRUCTION for DIPLOMA Students.pdf
 
Phase noise transfer functions.pptx
Phase noise transfer      functions.pptxPhase noise transfer      functions.pptx
Phase noise transfer functions.pptx
 
SATELITE COMMUNICATION UNIT 1 CEC352 REGULATION 2021 PPT BASICS OF SATELITE ....
SATELITE COMMUNICATION UNIT 1 CEC352 REGULATION 2021 PPT BASICS OF SATELITE ....SATELITE COMMUNICATION UNIT 1 CEC352 REGULATION 2021 PPT BASICS OF SATELITE ....
SATELITE COMMUNICATION UNIT 1 CEC352 REGULATION 2021 PPT BASICS OF SATELITE ....
 
me3493 manufacturing technology unit 1 Part A
me3493 manufacturing technology unit 1 Part Ame3493 manufacturing technology unit 1 Part A
me3493 manufacturing technology unit 1 Part A
 
Présentation IIRB 2024 Chloe Dufrane.pdf
Présentation IIRB 2024 Chloe Dufrane.pdfPrésentation IIRB 2024 Chloe Dufrane.pdf
Présentation IIRB 2024 Chloe Dufrane.pdf
 

Your code are my tests