SlideShare a Scribd company logo
Create, Test, Secure &
Repeat
Part of the in2it Quality Assurance Training
in it2PROFESSIONAL PHP SERVICES
https://www.flickr.com/photos/90371939@N00/4344878104
Michelangelo van Dam
PHP Consultant, Community Leader & Trainer
https://www.flickr.com/photos/akrabat/8784318813
Create, Test, Secure, Repeat
Workshop
Get prepared
https://github.com/in2it/ctsr-workshop
Requirements
5.4+
Requirements
- a computer with PHP 5.4 or higher
- the latest composer
- git
Exercises tested on
- Microsoft Windows 7 & 8
- Linux
- Mac OS X 10.10.2 or higher
Learn unit testing like a pro
https://www.flickr.com/photos/eriktorner/8048428729
Introduction
Mise en place (preparation)
Running Tests
Starting a new project with TDD
Testing & Improving legacy code
Other tools
Recap & Closing remarks
https://www.flickr.com/photos/ryantylersmith/14010104872
Introduction
Mise en place (preparation)
Running Tests
Starting a new project with TDD
Testing & Improving legacy code
Other tools
Recap & Closing remarks
https://www.flickr.com/photos/ryantylersmith/14010104872
PHPUnit
• Created by Sebastian
Bergmann in 2004
• Port of xUnit to PHP
• Uses assertions for testing
• Supports
• Unit testing
• Integration testing w/ DBUnit
• Acceptance testing w/
Selenium
https://www.flickr.com/photos/greenboy/3895219425
https://www.flickr.com/photos/greenboy/3895219425
https://www.flickr.com/photos/tonivc/
https://www.flickr.com/photos/greenboy/3895219425
https://www.flickr.com/photos/tonivc/
https://www.flickr.com/photos/68751915@N05/6736158045
https://www.flickr.com/photos/akrabat/8421560178
https://www.flickr.com/photos/jakerust/16223669794
A little test
Who created PHPUnit and is
now project lead?
A. Chris Hartjes
B. Sebastian Bergmann
C. Stefan Priepsch
With PHPUnit you can…?
A. Test the smallest functional piece of code (unit)
B. Test integrations with a database (integration)
C. Test automated acceptance testing with Selenium
D. All of the above
E. None of the above
An assertion is…?
A. Verifying that an expected value matches the
result of a process?
B. Verifying that a process produces results
C. A transformation of a value
Assertion
• is a true/false statement
• to match an expectation
• with the result of functionality under test
Available assertions
• assertArrayHasKey()
• assertArraySubset()
• 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()
• assertStringEndsWith()
• assertStringEqualsFile()
• assertStringStartsWith()
• assertThat()
• assertTrue()
• assertXmlFileEqualsXmlFile()
• assertXmlStringEqualsXmlFile()
• assertXmlStringEqualsXmlString()
Available assertions
• assertArrayHasKey()
• assertArraySubset()
• 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()
• assertStringEndsWith()
• assertStringEqualsFile()
• assertStringStartsWith()
• assertThat()
• assertTrue()
• assertXmlFileEqualsXmlFile()
• assertXmlStringEqualsXmlFile()
• assertXmlStringEqualsXmlString()
Example usage
Assertions
// Asserting a value returned by $myClass->myMethod() is TRUE
$this->assertTrue($myClass->myMethod());
// Asserting that a string matches type and value of $myClass->toString()
$this->assertSame('my string', $myClass->toString());
// Asserting that the value matches $myClass-
>addOne(0), type check not necessary
$this->assertEquals('1', $myClass->addOne(0));
// Assserting that the result of $myClass->getBirthday()->format('Y') 
// is greater than the expected value
$this->assertGreaterThan(1900, $myClass->getBirthday()->format('Y'));
// Asserting a value is NULL with a specified error message
$this->assertNull(
    $myClass->getProperty(), 
    'When instantiating MyClass the property value should be NULL but is 
' . $myClass->getProperty()
);
Annotations
• Provide automatic features
• to execute arbitrary functionality
• without having to create logic
Available annotations
• @author
• @after
• @afterClass
• @backupGlobals
• @backupStaticAttributes
• @before
• @beforeClass
• @codeCoverageIgnore*
• @covers
• @coversDefaultClass
• @coversNothing
• @dataProvider
• @depends
• @expectedException
• @expectedExceptionCode
• @expectedExceptionMessage
• @expectedExceptionMessageRegExp
• @group
• @large
• @medium
• @preserveGlobalState
• @requires
• @runTestsInSeparateProcesses
• @runInSeparateProcess
• @small
• @test
• @testdox
• @ticket
• @uses
@group
<?php
class OrderTest extends PHPUnit_Framework_TestCase
{
    /**
     * @group Order
     */
    public function testCanCreateOrder()
    {
        // ... test logic goes here
    }
    
    /**
     * @group Order
     * @group BUG-1234
     */
    public function testOrdersCanNotContainSoldProducts()
    {
        // ... test logic goes here
    }
}
How to use @group
# Run phpunit only against tests for the Order module
./vendor/bin/phpunit --group Order
# Run phpunit for all tests except for the Order module
./vendor/bin/phpunit --exclude-group Order
@dataProvider
<?php
class OrderTest extends PHPUnit_Framework_TestCase
{
    public function badDataProvider()
    {
        return [
            [0, 0, 0], // we don't accept ID's less or equal than 0
            ['', new stdClass(), []], // only integer and float values
            [null, null, null], // no NULL values allowed
        ];
    }
    
    /**
     * @dataProvider badDataProvider
     */
    public function testAddProductToOrder($orderId, $productId, $price)
    {
        $order = new Order();
        $result = $order->addProductToOrder($orderId, $productId, $price);
        $this->assertFalse($result);
    }
}
@expectedException
<?php
class OrderTest extends PHPUnit_Framework_TestCase
{
    /**
     * @expectedException InvalidArgumentException
     * @expectedExceptionMessage The order ID cannot be null
     */
    public function testAddProductToOrder()
    {
        $orderId = null;
        $productId = null;
        $price = null;
        $order = new Order();
        $result = $order->addProductToOrder($orderId, $productId, $price);
        $this->fail('Expected exception was not thrown');
    }
}
www.phpunit.de
Introduction
Mise en place (preparation)
Running Tests
Starting a new project with TDD
Testing & Improving legacy code
Other tools
Recap & Closing remarks
https://www.flickr.com/photos/ryantylersmith/14010104872
https://www.flickr.com/photos/427/2253530041
Getting PHPUnit
Composer way (preferred)
Download Composer

curl -sS https://getcomposer.org/installer | php
Get phpunit

php composer.phar require phpunit/phpunit
Direct download
curl -O https://phar.phpunit.de/phpunit.phar
https://www.flickr.com/photos/rueful/5906546599
For this training
Installation of source code
Clone the repository into your workspace before attending the workshop.

git clone https://github.com/in2it/ctsr-workshop.git
cd ctsr-workshop/
Once you have cloned the training package, make sure you install composer.

curl -sS https://getcomposer.org/installer | php
When the download is done, install required components using composer

php composer.phar install
For this training
Installation of source code
Clone the repository into your workspace before attending the workshop.

git clone https://github.com/in2it/ctsr-workshop.git
cd ctsr-workshop/
Once you have cloned the training package, make sure you install composer.

curl -sS https://getcomposer.org/installer | php
When the download is done, install required components using composer

php composer.phar install
https://www.flickr.com/photos/intelfreepress/13983474320
During the workshop you're asked to solve several exercises. All example codes
are based on a UNIX-like OS, so if you plan to participate this workshop with
another OS, you need to know what changes are required to have the exercises
run on your operating system.

The exercises, the source code and the examples are tested on the following
platforms:

	 •	 Windows 7

	 •	 Mac OS X

	 •	 Ubuntu Linux

When you need to switch to a specific exercise branch (e.g. ex-0.0), you can do
this with the following command.

git checkout -b ex-0.0 origin/ex-0.0
https://www.flickr.com/photos/rhinoneal/8060238470
phpunit.xml
<?xml	
  version="1.0"	
  encoding="utf-­‐8"?>	
  
<phpunit	
  
	
  	
  	
  	
  bootstrap="./vendor/autoload.php"	
  
	
  	
  	
  	
  colors="true"	
  
	
  	
  	
  	
  stopOnFailure="true"	
  
	
  	
  	
  	
  stopOnError="true"	
  
	
  	
  	
  	
  syntaxCheck="true">	
  
	
  	
  	
  	
  <testsuite	
  name="Unit	
  Tests">	
  
	
  	
  	
  	
  	
  	
  	
  	
  <directory>./tests</directory>	
  
	
  	
  	
  	
  </testsuite>	
  
	
  	
  	
  	
  <filter>	
  
	
  	
  	
  	
  	
  	
  	
  	
  <whitelist>	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  <directory	
  suffix=".php">./src</directory>	
  
	
  	
  	
  	
  	
  	
  	
  	
  </whitelist>	
  
	
  	
  	
  	
  </filter>	
  
	
  	
  	
  	
  <logging>	
  
	
  	
  	
  	
  	
  	
  	
  	
  <log	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  type="coverage-­‐html"	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  target="./build/coverage"	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  charset="UTF-­‐8"	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  yui="true"	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  highlight="true"	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  lowUpperBound="35"	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  highLowerBound="70"/>	
  
	
  	
  	
  	
  	
  	
  	
  	
  <log	
  type="coverage-­‐xml"	
  target="./build/logs/coverage.xml"/>	
  
	
  	
  	
  	
  	
  	
  	
  	
  <log	
  type="coverage-­‐clover"	
  target="./build/logs/clover.xml"/>	
  
	
  	
  	
  	
  	
  	
  	
  	
  <log	
  type="tap"	
  target="./build/logs/phpunit.tap"/>	
  
	
  	
  	
  	
  	
  	
  	
  	
  <log	
  type="testdox-­‐text"	
  target="./build/logs/testdox.txt"/>	
  
	
  	
  	
  	
  	
  	
  	
  	
  <log	
  type="junit"	
  target="./build/logs/junit.xml"	
  logIncompleteSkipped="false"/>	
  
	
  	
  	
  	
  </logging>	
  
</phpunit>
Introduction
Mise en place (preparation)
Running Tests
Starting a new project with TDD
Testing & Improving legacy code
Other tools
Recap & Closing remarks
https://www.flickr.com/photos/ryantylersmith/14010104872
https://www.flickr.com/photos/cgc/6776701
Composer PHPUnit
./vendor/bin/phpunit
Phar PHPUnit
php phpunit.phar
Exercise 0.0
• What will happen when you run PHPUnit now?
• Checkout branch ex-0.0
git checkout -b ex-0.0 origin/ex-0.0
php composer.phar dump-autoload
• Run PHPUnit
Let’s write our first test
<?php
namespace In2itTestWorkshopCtsr;
use In2itWorkshopCtsrSampleClass;
class SampleClassTest extends PHPUnit_Framework_TestCase
{
    public function testSomethingReturnsGreeting()
    {
        $sampleClass = new SampleClass();
        $this->assertSame(
            'Hello World!', $sampleClass->doSomething()
        );
    }
}
Exercise 0.1
• What will happen when you run PHPUnit now?
• Checkout branch ex-0.1
git checkout -b ex-0.1 origin/ex-0.1
php composer.phar dump-autoload
• Run PHPUnit
Error?
Got Error?
error: Your local changes to the following files would be overwritten by checkout:
composer.lock
Please, commit your changes or stash them before you can switch branches.
Aborting
Solution
git checkout -- composer.lock
git checkout -b ex-0.1 origin/ex-0.1
Write our class
<?php
namespace In2itWorkshopCtsr;
class SampleClass
{
    public function doSomething()
    {
        return 'Hello World!';
    }
}
Exercise 0.2
• What will happen when you run PHPUnit now?
• Checkout branch ex-0.2
git checkout -b ex-0.2 origin/ex-0.2
php composer.phar dump-autoload
• Run PHPUnit
Exercise 0.3
• Test that you can provide an argument and the
argument will be returned as “Hello <arg>!”
• Write the test
• Modify the class
Modifying the test class
    public function testSomethingReturnsArgument()
    {
        $sampleClass = new SampleClass();
        $argument = 'Class';
        $this->assertSame(
            sprintf('Hello %s!', $argument),
            $sampleClass->doSomething($argument)
        );
    }
Modify the SampleClass
<?php
namespace In2itWorkshopCtsr;
class SampleClass
{
    public function doSomething($argument)
    {
        return 'Hello ' . $argument . '!';
    }
}
Update further
<?php
namespace In2itWorkshopCtsr;
class SampleClass
{
    public function doSomething($argument = 'World')
    {
        return 'Hello ' . $argument . '!';
    }
}
Chapter 0
What have you learned
• How to install phpunit
• How to configure phpunit
• How to write your test first
• How to modify requirements through testing
• How to debug failures and fix them easily with tests
https://www.flickr.com/photos/raster/3563135804
Introduction
Mise en place (preparation)
Running Tests
Starting a new project with TDD
Testing & Improving legacy code
Other tools
Recap & Closing remarks
https://www.flickr.com/photos/ryantylersmith/14010104872
https://www.flickr.com/photos/esoterika/5347998757
Schedule Manager
Functional requirements
• Application needs to manage cron entries, where
all scheduled tasks are stored in a database and
written to the crontab when saved.
What is crontab?
• A tool on UNIX-like systems to execute tasks at a certain interval
• Each line contains the following items:
• Minutes and/or interval
• Hours and/or interval
• Days of the month (DOM) and/or interval
• Months and/or interval
• Days of the week (DOW) and/or interval
• Executable command
Example crontab
# Minutes Hours DOM Months DOW Command
# Warm up caches with new products
# Every 5 minutes each day
*/5 * * * * /bin/sh /path/to/productCollector.sh 2>&1
# Send marketing mail to active customers
# Every Monday at 9:30am
30 9 * * 1 /usr/bin/php /path/to/marketingMailSender.php 2>&1
# Clean up waste
# Every day at 6am, 1pm and 5pm from Monday to Friday
0 6,13,17 * * 1-5 /bin/sh /path/to/wasteCleaner.sh 2>&1
Analysis
• crontab = collection of entries
• Each entry contains 5 assets and a command
• Each asset can contain a
• Wildcard (full range)
• Range n-m (subset of full range)
• List n,m,o
• A single value n
• With similar intervals (without the wildcard)
https://www.flickr.com/photos/caveman_92223/3968354387
• cronmanager
• collection of crontab entries
• each entry contains
• minutes (collection) and interval (collection)
• hours (collection) and interval (collection)
• days of the month (collection) and interval (collection)
• months (collection) and interval (collection)
• days of the week (collection) and interval (collection)
• command string
• each entry collection (minutes, hours, dom, months, dow) requires a range
• minutes (0 - 59)
• hours (0 - 23)
• days of the month (1 - 31)
• month (1 - 12)
• days of the week (0 - 7) (0 & 7 are Sunday)
• crontab is write-only, so we need to update the full crontab completely
<?php
namespace In2itTestWorkshopCtsr;
class CronManagerTest extends PHPUnit_Framework_TestCase
{
    public function testCronManagerCanAddAnEntry()
    {
        $entry = new stdClass();
        $cronman = new CronManager();
        $cronman->addEntry($entry);
        $this->assertCount(1, $cronman);
    }
}
<?php
namespace In2itWorkshopCtsr;
class CronManager implements Countable, Iterator
{
    protected $stack;
    protected $pointer;
    protected $counter;
    public function addEntry($entry)
    {
        $this->stack[] = $entry;
        $this->counter++;
    }
    // Implement the Iterator and Countable methods here.
    // - Iterator: http://php.net/manual/en/class.iterator.php
    // - Countable: http://php.net/manual/en/class.countable.php 
    public function current() {/**... */}
    public function next() {/**... */}
    public function key() {/**... */}
    public function valid() {/**... */}
    public function rewind() {/**... */}
    public function count() {/**... */}
}
<?php
namespace In2itTestWorkshopCtsr;
use In2itWorkshopCtsrCronManager;
class CronManagerTest extends PHPUnit_Framework_TestCase
{
    public function testCronManagerCanAddAnEntry()
    {
        $entry = new stdClass();
        $cronman = new CronManager();
        $cronman->addEntry($entry);
        $this->assertCount(1, $cronman);
    }
}
<?php
namespace In2itTestWorkshopCtsr;
use In2itWorkshopCtsrCronManager;
class CronManagerTest extends PHPUnit_Framework_TestCase
{
    public function testCronManagerCanAddAnEntry()
    {
        $entry = new stdClass();
        $cronman = new CronManager();
        $cronman->addEntry($entry);
        $this->assertCount(1, $cronman);
    }
}
Exercise 1.0
• Checkout branch ex-1.0
• Create a test for the Entry class
Something like this…
<?php
namespace In2itTestWorkshopCtsr;
use In2itWorkshopCtsrCronManagerEntry;
class EntryTest extends PHPUnit_Framework_TestCase
{
    public function testEntryContainsAllFields() { /** ... */ }
    public function testEntryCanSetEntryElements() { /** ... */ }
}
Something like this… (2)
public function testEntryContainsAllFields()
{
    $entry = new Entry();
    $this->assertCount(0, $entry->getMinutes());
    $this->assertCount(0, $entry->getHours());
    $this->assertCount(0, $entry->getDom());
    $this->assertCount(0, $entry->getMonths());
    $this->assertCount(0, $entry->getDow());
    $this->assertSame('', $entry->getCommand());
}
Something like this… (3)
public function testEntryCanSetEntryElements()
{
    $assetCollection = $this->getMock(
        'In2itWorkshopCtsrCronManagerAssetCollection'
    );
    $entry = new Entry();
    $entry->setMinutes($assetCollection);
    $this->assertInstanceOf(
        'In2itWorkshopCtsrCronManagerAssetCollection', 
        $entry->getMinutes()
    );
    /* 
     * Similar routines for Hours, Days of the Month, Months and Days of the week
     */
    $command = $this->getMock('In2itWorkshopCtsrCronManagerCommand');
    $entry->setCommand($command);
    $this->assertInstanceOf(
        'In2itWorkshopCtsrCronManagerCommand', 
        $entry->getCommand()
    );
}
Overview of classes (ex-1.0)
ctsr-workshop/
src/
ex-1.0/
CronManager.php
tests/
ex-1.0/
CronManager/
EntryTest.php
CronManagerTest.php
Exercise 1.1
• Checkout branch ex-1.1
• Have a look at all the tests
Pop-quiz
// Why are we using Mock objects to test functionality?
$assetCollection = $this->getMock(
    'In2itWorkshopCtsrCronManagerAssetCollection'
);
$asset = $this->getMock(
    'In2itWorkshopCtsrCronManagerAsset'
);
$entry = $this->getMock(
    'In2itWorkshopCtsrCronManagerEntry'
);
Question
• Are we protected against bad input?
• Yes
• No
Create some bad data
public function badDataProvider()
{
    return array (
        array ('foo'),
        array (new stdClass()),
        array (array ()),
        array (1.50),
    );
}
And let’s test it!
/**
 * @dataProvider badDataProvider
 * @covers In2itWorkshopCtsrCronManagerAsset::__construct
 * @covers In2itWorkshopCtsrCronManagerAsset::setValue
 * @covers In2itWorkshopCtsrCronManagerAsset::getValue
 * @expectedException InvalidArgumentException
 */
public function testRejectBadData($badData)
{
    $asset = new Asset($badData);
    $this->fail('Expected InvalidArgumentException to be thrown');
}
Let’s fix that!
/**
 * @param int $value
 * @throws InvalidArgumentException
 */
public function setValue($value)
{
    if (!is_int($value)) {
        throw new InvalidArgumentException(
            'You've provided an invalid argument'
        );
    }
    if (false === ($result = filter_var($value, FILTER_VALIDATE_INT))) {
        throw new InvalidArgumentException(
            'You've provided an invalid argument'
        );
    }
    $this->value = (int) $value;
}
Complete code in
branch ex-1.2
Introduction
Mise en place (preparation)
Running Tests
Starting a new project with TDD
Testing & Improving legacy code
Other tools
Recap & Closing remarks
https://www.flickr.com/photos/ryantylersmith/14010104872
https://www.flickr.com/photos/cmdrcord/9645186380
Legacy code
• Code that was already written
• Not (always) adhering to best
practices
• Not (always) testable
• What developers hate working
on
https://www.flickr.com/photos/archer10/7845300746
<?php
class ModuleManager
{
    public static $modules_install = array();
    /**
     * Includes file with module installation class.
     *
     * Do not use directly.
     *
     * @param string $module_class_name module class name - underscore separated
     * @return bool
     */
    public static final function include_install($module_class_name) {
        if(isset(self::$modules_install[$module_class_name])) return true;
        $full_path = __DIR__ . '/modules/' . $module_class_name . '/' . $module_class_name . '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 ' . $module_class_name . ': Invalid install file', E_USER_ERROR);
        self::$modules_install[$module_class_name] = new $x($module_class_name);
        return true;
    }
}
<?php
include_once __DIR__ . '/../../src/ex-2.0/ModuleManager.php';
class ModuleManagerTest extends PHPUnit_Framework_TestCase
{
    /**
     * @covers ModuleManager::include_install
     */
    public function testModuleManagerCanLoadMailModule()
    {
        $result = ModuleManager::include_install('Mail');
        $this->assertTrue($result);
    }
}
<?php
class ModuleManager
{
    public static $modules_install = array();
    /**
     * Includes file with module installation class.
     *
     * Do not use directly.
     *
     * @param string $module_class_name module class name - underscore separated
     * @return bool
     */
    public static final function include_install($module_class_name) {
        if(isset(self::$modules_install[$module_class_name])) return true;
        $full_path = __DIR__ . '/modules/' . $module_class_name . '/' . $module_class_name . '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 ' . $module_class_name . ': Invalid install file', E_USER_ERROR);
        self::$modules_install[$module_class_name] = new $x($module_class_name);
        return true;
    }
}
<?php
class ModuleManager
{
    public static $modules_install = array();
    /**
     * Includes file with module installation class.
     *
     * Do not use directly.
     *
     * @param string $module_class_name module class name - underscore separated
     * @return bool
     */
    public static final function include_install($module_class_name) {
        if(isset(self::$modules_install[$module_class_name])) return true;
        $full_path = __DIR__ . '/modules/' . $module_class_name . '/' . $module_class_name . '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 ' . $module_class_name . ': Invalid install file', E_USER_ERROR);
        self::$modules_install[$module_class_name] = new $x($module_class_name);
        return true;
    }
}
<?php
class ModuleManager
{
    public static $modules_install = array();
    /**
     * Includes file with module installation class.
     *
     * Do not use directly.
     *
     * @param string $module_class_name module class name - underscore separated
     * @return bool
     */
    public static final function include_install($module_class_name) {
        if(isset(self::$modules_install[$module_class_name])) return true;
        $full_path = __DIR__ . '/modules/' . $module_class_name . '/' . $module_class_name . '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 ' . $module_class_name . ': Invalid install file', E_USER_ERROR);
        self::$modules_install[$module_class_name] = new $x($module_class_name);
        return true;
    }
}
<?php
class ModuleManager
{
    public static $modules_install = array();
    /**
     * Includes file with module installation class.
     *
     * Do not use directly.
     *
     * @param string $module_class_name module class name - underscore separated
     * @return bool
     */
    public static final function include_install($module_class_name) {
        if(isset(self::$modules_install[$module_class_name])) return true;
        $full_path = __DIR__ . '/modules/' . $module_class_name . '/' . $module_class_name . '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 ' . $module_class_name . ': Invalid install file', E_USER_ERROR);
        self::$modules_install[$module_class_name] = new $x($module_class_name);
        return true;
    }
}
<?php
class ModuleManager
{
    public static $modules_install = array();
    /**
     * Includes file with module installation class.
     *
     * Do not use directly.
     *
     * @param string $module_class_name module class name - underscore separated
     * @return bool
     */
    public static final function include_install($module_class_name) {
        if(isset(self::$modules_install[$module_class_name])) return true;
        $full_path = __DIR__ . '/modules/' . $module_class_name . '/' . $module_class_name . '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 ' . $module_class_name . ': Invalid install file', E_USER_ERROR);
        self::$modules_install[$module_class_name] = new $x($module_class_name);
        return true;
    }
}
<?php
include_once __DIR__ . '/../../src/ex-2.0/ModuleManager.php';
class ModuleManagerTest extends PHPUnit_Framework_TestCase
{
    /**
     * @covers ModuleManager::include_install
     */
//    public function testModuleManagerCanLoadMailModule()
//    {
//        $result = ModuleManager::include_install('Mail');
//        $this->assertTrue($result);
//    }
}
https://www.flickr.com/photos/marcgbx/7803086292
/**
 * @covers ModuleManager::include_install
 */
public function testReturnImmediatelyWhenModuleAlreadyLoaded()
{
    $module = 'Foo_Bar';
    ModuleManager::$modules_install[$module] = 1;
    $result = ModuleManager::include_install($module);
    $this->assertTrue($result);
    $this->assertCount(1, ModuleManager::$modules_install);
}
https://www.flickr.com/photos/christian_johannesen/2248244786
/**
 * @covers ModuleManager::include_install
 */
public function testReturnWhenModuleIsNotFound()
{
    $module = 'Foo_Bar';
    $result = ModuleManager::include_install($module);
    $this->assertFalse($result);
    $this->assertEmpty(ModuleManager::$modules_install);
}
public static final function include_install($module_class_name) {
    if(isset(self::$modules_install[$module_class_name])) return true;
    $full_path = __DIR__ . '/modules/' . $module_class_name . '/' . $module_class_name . '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 ' . $module_class_name . ': Invalid install file', E_USER_ERROR);
    self::$modules_install[$module_class_name] = new $x($module_class_name);
    return true;
}
self::$modules_install[$module_class_name]
protected function tearDown()
{
    ModuleManager::$modules_install = array ();
}
https://www.flickr.com/photos/evaekeblad/14780090550
/**
 * @covers ModuleManager::include_install
 * @expectedException PHPUnit_Framework_Error
 */
public function testTriggerErrorWhenInstallClassDoesNotExists()
{
    $module = 'EssClient';
    $result = ModuleManager::include_install($module);
    $this->fail('Expecting loading module EssClient would trigger and error');
}
public static final function include_install($module_class_name) {
    if(isset(self::$modules_install[$module_class_name])) return true;
    $full_path = __DIR__ . '/modules/' . $module_class_name . '/' . $module_class_name . '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 ' . $module_class_name . ': Invalid install file', E_USER_ERROR);
    self::$modules_install[$module_class_name] = new $x($module_class_name);
    return true;
}
public static final function include_install($module_class_name) {
    if(isset(self::$modules_install[$module_class_name])) return true;
    $full_path = __DIR__ . '/modules/' . $module_class_name . '/' . $module_class_name . '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 ' . $module_class_name . ': Invalid install file', E_USER_ERROR);
    self::$modules_install[$module_class_name] = new $x($module_class_name);
    return true;
}
if (!file_exists($full_path)) return false;
Current Filestructure
|-- ModuleManager.php
`-- modules
    |-- EssClient
    |   `-- EssClient.php
    |-- IClient
    |   `-- IClientInstall.php
    `-- Mail
        `-- MailInstall.php
https://www.flickr.com/photos/sis/2497912343
https://www.flickr.com/photos/fragiletender/5332586299
Current Filestructure
|-- ModuleManager.php
`-- modules
    |-- EssClient
    |   `-- EssClient.php
    |-- IClient
    |   `-- IClientInstall.php
    `-- Mail
        `-- MailInstall.php
/**
 * @covers ModuleManager::include_install
 * @expectedException PHPUnit_Framework_Error
 */
public function testTriggerErrorWhenInstallClassDoesNotExists()
{
    $module = 'IClient';
    $result = ModuleManager::include_install($module);
    $this->fail('Expecting loading module EssClient would trigger and error');
}
/**
 * @covers ModuleManager::include_install
 */
public function testModuleManagerCanLoadMailModule()
{
    $result = ModuleManager::include_install('Mail');
    $this->assertTrue($result);
}
Get the code
branch ex-2.0
https://www.flickr.com/photos/ahhyeah/454494396
What to do
• Your legacy code has no return values?
    /**
     * Process Bank Payment files
     */
    public function processBankPayments()
    {
        $this->getLogger()->log('Starting bank payment process', Zend_Log::INFO);
        foreach ($this->_getBankFiles() as $bankFile) {
            $bankData = $this->_processBankFile($bankFile);
            $this->getLogger()->log('Processing ' . $bankData->transactionId,
                Zend_Log::DEBUG
            );
            /** @var Contact_Model_Contact $contact */
            $contact = $this->getMapper('Contact_Model_Mapper_Contact')
                ->findContactByBankAccount($bankData->transactionAccount);
            if (null !== $contact) {
                $this->getLogger()->log(sprintf(
                    'Found contact "%s" for bank account %s',
                    $contact->getName(),
                    $bankData->transactionAccount
                ), Zend_Log::DEBUG);
                $data = array (
                    'amount' => $bankData->transactionAmount,
                    'payment_date' => $bankData->transactionDate
                );
                $this->getMapper('Invoice_Model_Mapper_Payments')
                    ->updatePayment($data,
                        array ('contact_id = ?' => $contact->getContactId())
                    );
                $this->_moveBankFile($bankFile,
                    $this->getPath() . DIRECTORY_SEPARATOR . self::PROCESS_SUCCEEDED
                );
            } else {
                $this->getLogger()->log(sprintf(
                    'Could not match bankaccount "%s" with a contact',
                    $bankData->transactionAccount
                ), Zend_Log::WARN);
                $this->_moveBankFile($bankFile,
                    $this->getPath() . DIRECTORY_SEPARATOR . self::PROCESS_FAILED
                );
            }
        }
    }
    public function testProcessingBankPayments()
    {
        $contact = $this->getMock(
            'Contact_Model_Contact',
            array ('getContactId', 'getName')
        );
        $contact->expects($this->any())
            ->method('getContactId')
            ->will($this->returnValue(1));
        $contact->expects($this->any())
            ->method('getName')
            ->will($this->returnValue('Foo Bar'));
        $contactMapper = $this->getMock('Contact_Model_Mapper_Contact',
            array ('findContactByBankAccount')
        );
        $contactMapper->expects($this->any())
            ->method('findContactByBankAccount')
            ->will($this->returnValue($contact));
        $paymentsMapper = $this->getMock('Invoice_Model_Mapper_Payments',
            array ('updatePayment')
        );
        $logMock = new Zend_Log_Writer_Mock();
        $logger = new Zend_Log();
        $logger->setWriter($logMock);
        $logger->setPriority(Zend_Log::DEBUG);
        $as400 = new Payments_Service_As400();
        $as400->addMapper($contactMapper, 'Contact_Model_Mapper_Contact')
            ->addMapper($paymentsMapper, 'Invoice_Model_Mapper_Payments')
            ->setPath(__DIR__ . DIRECTORY_SEPARATOR . '_files')
            ->setLogger($logger);
        $as400->processBankPayments();
        $this->assertCount(3, $logMock->events);
        $this->assertEquals('Processing 401341345', $logMock->events[1]);
        $this->assertEquals(
            'Found contact "Foo Bar" for bank account BE93522511513933',
            $logMock->events[2]
        );
    }
        $as400 = new Payments_Service_As400();
        $as400->addMapper($contactMapper, 'Contact_Model_Mapper_Contact')
            ->addMapper($paymentsMapper, 'Invoice_Model_Mapper_Payments')
            ->setPath(__DIR__ . DIRECTORY_SEPARATOR . '_files')
            ->setLogger($logger);
        $as400->processBankPayments();
        $this->assertCount(3, $logMock->events);
        $this->assertEquals('Processing 401341345', $logMock->events[1]);
        $this->assertEquals(
            'Found contact "Foo Bar" for bank account BE93522511513933',
            $logMock->events[2]
        );
Get the code
branch ex-2.1
https://www.flickr.com/photos/ahhyeah/454494396
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
Introduction
Mise en place (preparation)
Running Tests
Starting a new project with TDD
Testing & Improving legacy code
Other tools
Recap & Closing remarks
https://www.flickr.com/photos/ryantylersmith/14010104872
https://www.flickr.com/photos/florianric/7263382550
SCM is a must!
FTP is not a SCM
Use Composer
Automation tools
https://www.gnu.org/graphics/heckert_gnu.small.png
Common CI systems
Online CI systems
Online CI systems
Online CI systems
Some other test tools
https://www.gnu.org/graphics/heckert_gnu.small.png
Introduction
Mise en place (preparation)
Running Tests
Starting a new project with TDD
Testing & Improving legacy code
Other tools
Recap & Closing remarks
https://www.flickr.com/photos/ryantylersmith/14010104872
https://www.flickr.com/photos/didmyself/8030013349
https://www.flickr.com/photos/wjserson/3310851114
https://www.flickr.com/photos/joeshlabotnik/2384495536
https://www.flickr.com/photos/much0/8552353901
https://www.flickr.com/photos/thomashawk/10490113913
References
in it2PROFESSIONAL PHP SERVICES
Michelangelo van Dam
Zend Certified Engineer
training@in2it.be - www.in2it.be - T in2itvof - F in2itvof
PHPUnit
Getting Started
Advanced Testing
Zend Framework 2
Fundamentals
Advanced
Azure PHP
Quick time to market
Scale up and out
jQuery
Professional jQuery
PHP
PHP for beginners
Professional PHP
HTML & CSS
The Basics
Our training courses
https://www.flickr.com/photos/drewm/3191872515

More Related Content

What's hot

Selenium Clinic Eurostar 2012 WebDriver Tutorial
Selenium Clinic Eurostar 2012 WebDriver TutorialSelenium Clinic Eurostar 2012 WebDriver Tutorial
Selenium Clinic Eurostar 2012 WebDriver Tutorial
Alan Richardson
 
Nightwatch at Tilt
Nightwatch at TiltNightwatch at Tilt
Nightwatch at Tilt
Dave King
 
Testing Web Applications
Testing Web ApplicationsTesting Web Applications
Testing Web Applications
Seth McLaughlin
 
Five Easy Ways to QA Your Drupal Site
Five Easy Ways to QA Your Drupal SiteFive Easy Ways to QA Your Drupal Site
Five Easy Ways to QA Your Drupal Site
Mediacurrent
 
Continuous Integration Testing in Django
Continuous Integration Testing in DjangoContinuous Integration Testing in Django
Continuous Integration Testing in Django
Kevin Harvey
 
Automated Frontend Testing
Automated Frontend TestingAutomated Frontend Testing
Automated Frontend Testing
Neil Crosby
 
Unit testing - A&BP CC
Unit testing - A&BP CCUnit testing - A&BP CC
Unit testing - A&BP CC
JWORKS powered by Ordina
 
Front-End Testing: Demystified
Front-End Testing: DemystifiedFront-End Testing: Demystified
Front-End Testing: Demystified
Seth McLaughlin
 
WebTest - Efficient Functional Web Testing with HtmlUnit and Beyond
WebTest - Efficient Functional Web Testing with HtmlUnit and BeyondWebTest - Efficient Functional Web Testing with HtmlUnit and Beyond
WebTest - Efficient Functional Web Testing with HtmlUnit and Beyond
mguillem
 
Unit-testing and E2E testing in JS
Unit-testing and E2E testing in JSUnit-testing and E2E testing in JS
Unit-testing and E2E testing in JS
Michael Haberman
 
Unit testing plugins: The 5 W's and an H
Unit testing plugins: The 5 W's and an HUnit testing plugins: The 5 W's and an H
Unit testing plugins: The 5 W's and an H
Tom Jenkins
 
One commit, one release. Continuously delivering a Symfony project.
One commit, one release. Continuously delivering a Symfony project.One commit, one release. Continuously delivering a Symfony project.
One commit, one release. Continuously delivering a Symfony project.
Javier López
 
Continous Delivering a PHP application
Continous Delivering a PHP applicationContinous Delivering a PHP application
Continous Delivering a PHP application
Javier López
 
vJUG - The JavaFX Ecosystem
vJUG - The JavaFX EcosystemvJUG - The JavaFX Ecosystem
vJUG - The JavaFX Ecosystem
Andres Almiray
 
Testing Automaton - CFSummit 2016
Testing Automaton - CFSummit 2016Testing Automaton - CFSummit 2016
Testing Automaton - CFSummit 2016
Ortus Solutions, Corp
 
Join the darkside: Selenium testing with Nightwatch.js
Join the darkside: Selenium testing with Nightwatch.jsJoin the darkside: Selenium testing with Nightwatch.js
Join the darkside: Selenium testing with Nightwatch.js
Seth McLaughlin
 
Никита Галкин "Testing in Frontend World"
Никита Галкин "Testing in Frontend World"Никита Галкин "Testing in Frontend World"
Никита Галкин "Testing in Frontend World"
Fwdays
 
Unit testing @ WordPress Meetup Tilburg 7 januari 2014
Unit testing @ WordPress Meetup Tilburg 7 januari 2014Unit testing @ WordPress Meetup Tilburg 7 januari 2014
Unit testing @ WordPress Meetup Tilburg 7 januari 2014
Barry Kooij
 
Codeception introduction and use in Yii
Codeception introduction and use in YiiCodeception introduction and use in Yii
Codeception introduction and use in Yii
IlPeach
 
Integration Testing in Python
Integration Testing in PythonIntegration Testing in Python
Integration Testing in Python
Panoptic Development, Inc.
 

What's hot (20)

Selenium Clinic Eurostar 2012 WebDriver Tutorial
Selenium Clinic Eurostar 2012 WebDriver TutorialSelenium Clinic Eurostar 2012 WebDriver Tutorial
Selenium Clinic Eurostar 2012 WebDriver Tutorial
 
Nightwatch at Tilt
Nightwatch at TiltNightwatch at Tilt
Nightwatch at Tilt
 
Testing Web Applications
Testing Web ApplicationsTesting Web Applications
Testing Web Applications
 
Five Easy Ways to QA Your Drupal Site
Five Easy Ways to QA Your Drupal SiteFive Easy Ways to QA Your Drupal Site
Five Easy Ways to QA Your Drupal Site
 
Continuous Integration Testing in Django
Continuous Integration Testing in DjangoContinuous Integration Testing in Django
Continuous Integration Testing in Django
 
Automated Frontend Testing
Automated Frontend TestingAutomated Frontend Testing
Automated Frontend Testing
 
Unit testing - A&BP CC
Unit testing - A&BP CCUnit testing - A&BP CC
Unit testing - A&BP CC
 
Front-End Testing: Demystified
Front-End Testing: DemystifiedFront-End Testing: Demystified
Front-End Testing: Demystified
 
WebTest - Efficient Functional Web Testing with HtmlUnit and Beyond
WebTest - Efficient Functional Web Testing with HtmlUnit and BeyondWebTest - Efficient Functional Web Testing with HtmlUnit and Beyond
WebTest - Efficient Functional Web Testing with HtmlUnit and Beyond
 
Unit-testing and E2E testing in JS
Unit-testing and E2E testing in JSUnit-testing and E2E testing in JS
Unit-testing and E2E testing in JS
 
Unit testing plugins: The 5 W's and an H
Unit testing plugins: The 5 W's and an HUnit testing plugins: The 5 W's and an H
Unit testing plugins: The 5 W's and an H
 
One commit, one release. Continuously delivering a Symfony project.
One commit, one release. Continuously delivering a Symfony project.One commit, one release. Continuously delivering a Symfony project.
One commit, one release. Continuously delivering a Symfony project.
 
Continous Delivering a PHP application
Continous Delivering a PHP applicationContinous Delivering a PHP application
Continous Delivering a PHP application
 
vJUG - The JavaFX Ecosystem
vJUG - The JavaFX EcosystemvJUG - The JavaFX Ecosystem
vJUG - The JavaFX Ecosystem
 
Testing Automaton - CFSummit 2016
Testing Automaton - CFSummit 2016Testing Automaton - CFSummit 2016
Testing Automaton - CFSummit 2016
 
Join the darkside: Selenium testing with Nightwatch.js
Join the darkside: Selenium testing with Nightwatch.jsJoin the darkside: Selenium testing with Nightwatch.js
Join the darkside: Selenium testing with Nightwatch.js
 
Никита Галкин "Testing in Frontend World"
Никита Галкин "Testing in Frontend World"Никита Галкин "Testing in Frontend World"
Никита Галкин "Testing in Frontend World"
 
Unit testing @ WordPress Meetup Tilburg 7 januari 2014
Unit testing @ WordPress Meetup Tilburg 7 januari 2014Unit testing @ WordPress Meetup Tilburg 7 januari 2014
Unit testing @ WordPress Meetup Tilburg 7 januari 2014
 
Codeception introduction and use in Yii
Codeception introduction and use in YiiCodeception introduction and use in Yii
Codeception introduction and use in Yii
 
Integration Testing in Python
Integration Testing in PythonIntegration Testing in Python
Integration Testing in Python
 

Viewers also liked

Hack the Future
Hack the FutureHack the Future
Hack the Future
Jason McCreary
 
Amp your site an intro to accelerated mobile pages
Amp your site  an intro to accelerated mobile pagesAmp your site  an intro to accelerated mobile pages
Amp your site an intro to accelerated mobile pages
Robert McFrazier
 
php[world] 2015 Training - Laravel from the Ground Up
php[world] 2015 Training - Laravel from the Ground Upphp[world] 2015 Training - Laravel from the Ground Up
php[world] 2015 Training - Laravel from the Ground Up
Joe Ferguson
 
Engineer - Mastering the Art of Software
Engineer - Mastering the Art of SoftwareEngineer - Mastering the Art of Software
Engineer - Mastering the Art of Software
Cristiano Diniz da Silva
 
Zend Framework Foundations
Zend Framework FoundationsZend Framework Foundations
Zend Framework Foundations
Chuck Reeves
 
Adding 1.21 Gigawatts to Applications with RabbitMQ (Bulgaria PHP 2016 - Tuto...
Adding 1.21 Gigawatts to Applications with RabbitMQ (Bulgaria PHP 2016 - Tuto...Adding 1.21 Gigawatts to Applications with RabbitMQ (Bulgaria PHP 2016 - Tuto...
Adding 1.21 Gigawatts to Applications with RabbitMQ (Bulgaria PHP 2016 - Tuto...
James Titcumb
 
Dip Your Toes in the Sea of Security
Dip Your Toes in the Sea of SecurityDip Your Toes in the Sea of Security
Dip Your Toes in the Sea of Security
James Titcumb
 
Console Apps: php artisan forthe:win
Console Apps: php artisan forthe:win Console Apps: php artisan forthe:win
Console Apps: php artisan forthe:win
Joe Ferguson
 
Code Coverage for Total Security in Application Migrations
Code Coverage for Total Security in Application MigrationsCode Coverage for Total Security in Application Migrations
Code Coverage for Total Security in Application Migrations
Dana Luther
 
Presentation Bulgaria PHP
Presentation Bulgaria PHPPresentation Bulgaria PHP
Presentation Bulgaria PHP
Alena Holligan
 
Git Empowered
Git EmpoweredGit Empowered
Git Empowered
Jason McCreary
 
Php extensions
Php extensionsPhp extensions
Php extensions
Elizabeth Smith
 
Conscious Coupling
Conscious CouplingConscious Coupling
Conscious Coupling
CiaranMcNulty
 
SunshinePHP 2017 - Making the most out of MySQL
SunshinePHP 2017 - Making the most out of MySQLSunshinePHP 2017 - Making the most out of MySQL
SunshinePHP 2017 - Making the most out of MySQL
Gabriela Ferrara
 
Modern sql
Modern sqlModern sql
Modern sql
Elizabeth Smith
 
200K+ reasons security is a must
200K+ reasons security is a must200K+ reasons security is a must
200K+ reasons security is a must
Michelangelo van Dam
 
Intermediate OOP in PHP
Intermediate OOP in PHPIntermediate OOP in PHP
Intermediate OOP in PHP
David Stockton
 
PHP World DC 2015 - What Can Go Wrong with Agile Development and How to Fix It
PHP World DC 2015 - What Can Go Wrong with Agile Development and How to Fix ItPHP World DC 2015 - What Can Go Wrong with Agile Development and How to Fix It
PHP World DC 2015 - What Can Go Wrong with Agile Development and How to Fix It
Matt Toigo
 
Enough suffering, fix your architecture!
Enough suffering, fix your architecture!Enough suffering, fix your architecture!
Enough suffering, fix your architecture!
Luís Cobucci
 
Website Accessibility: It’s the Right Thing to do
Website Accessibility: It’s the Right Thing to doWebsite Accessibility: It’s the Right Thing to do
Website Accessibility: It’s the Right Thing to do
DesignHammer
 

Viewers also liked (20)

Hack the Future
Hack the FutureHack the Future
Hack the Future
 
Amp your site an intro to accelerated mobile pages
Amp your site  an intro to accelerated mobile pagesAmp your site  an intro to accelerated mobile pages
Amp your site an intro to accelerated mobile pages
 
php[world] 2015 Training - Laravel from the Ground Up
php[world] 2015 Training - Laravel from the Ground Upphp[world] 2015 Training - Laravel from the Ground Up
php[world] 2015 Training - Laravel from the Ground Up
 
Engineer - Mastering the Art of Software
Engineer - Mastering the Art of SoftwareEngineer - Mastering the Art of Software
Engineer - Mastering the Art of Software
 
Zend Framework Foundations
Zend Framework FoundationsZend Framework Foundations
Zend Framework Foundations
 
Adding 1.21 Gigawatts to Applications with RabbitMQ (Bulgaria PHP 2016 - Tuto...
Adding 1.21 Gigawatts to Applications with RabbitMQ (Bulgaria PHP 2016 - Tuto...Adding 1.21 Gigawatts to Applications with RabbitMQ (Bulgaria PHP 2016 - Tuto...
Adding 1.21 Gigawatts to Applications with RabbitMQ (Bulgaria PHP 2016 - Tuto...
 
Dip Your Toes in the Sea of Security
Dip Your Toes in the Sea of SecurityDip Your Toes in the Sea of Security
Dip Your Toes in the Sea of Security
 
Console Apps: php artisan forthe:win
Console Apps: php artisan forthe:win Console Apps: php artisan forthe:win
Console Apps: php artisan forthe:win
 
Code Coverage for Total Security in Application Migrations
Code Coverage for Total Security in Application MigrationsCode Coverage for Total Security in Application Migrations
Code Coverage for Total Security in Application Migrations
 
Presentation Bulgaria PHP
Presentation Bulgaria PHPPresentation Bulgaria PHP
Presentation Bulgaria PHP
 
Git Empowered
Git EmpoweredGit Empowered
Git Empowered
 
Php extensions
Php extensionsPhp extensions
Php extensions
 
Conscious Coupling
Conscious CouplingConscious Coupling
Conscious Coupling
 
SunshinePHP 2017 - Making the most out of MySQL
SunshinePHP 2017 - Making the most out of MySQLSunshinePHP 2017 - Making the most out of MySQL
SunshinePHP 2017 - Making the most out of MySQL
 
Modern sql
Modern sqlModern sql
Modern sql
 
200K+ reasons security is a must
200K+ reasons security is a must200K+ reasons security is a must
200K+ reasons security is a must
 
Intermediate OOP in PHP
Intermediate OOP in PHPIntermediate OOP in PHP
Intermediate OOP in PHP
 
PHP World DC 2015 - What Can Go Wrong with Agile Development and How to Fix It
PHP World DC 2015 - What Can Go Wrong with Agile Development and How to Fix ItPHP World DC 2015 - What Can Go Wrong with Agile Development and How to Fix It
PHP World DC 2015 - What Can Go Wrong with Agile Development and How to Fix It
 
Enough suffering, fix your architecture!
Enough suffering, fix your architecture!Enough suffering, fix your architecture!
Enough suffering, fix your architecture!
 
Website Accessibility: It’s the Right Thing to do
Website Accessibility: It’s the Right Thing to doWebsite Accessibility: It’s the Right Thing to do
Website Accessibility: It’s the Right Thing to do
 

Similar to Create, test, secure, repeat

Fighting Fear-Driven-Development With PHPUnit
Fighting Fear-Driven-Development With PHPUnitFighting Fear-Driven-Development With PHPUnit
Fighting Fear-Driven-Development With PHPUnit
James Fuller
 
Testing NodeJS with Mocha, Should, Sinon, and JSCoverage
Testing NodeJS with Mocha, Should, Sinon, and JSCoverageTesting NodeJS with Mocha, Should, Sinon, and JSCoverage
Testing NodeJS with Mocha, Should, Sinon, and JSCoveragemlilley
 
Leveling Up With Unit Testing - php[tek] 2023
Leveling Up With Unit Testing - php[tek] 2023Leveling Up With Unit Testing - php[tek] 2023
Leveling Up With Unit Testing - php[tek] 2023
Mark Niebergall
 
Performance and Scalability Testing with Python and Multi-Mechanize
Performance and Scalability Testing with Python and Multi-MechanizePerformance and Scalability Testing with Python and Multi-Mechanize
Performance and Scalability Testing with Python and Multi-Mechanize
coreygoldberg
 
Automatic testing and quality assurance for WordPress plugins and themes
Automatic testing and quality assurance for WordPress plugins and themesAutomatic testing and quality assurance for WordPress plugins and themes
Automatic testing and quality assurance for WordPress plugins and themes
Otto Kekäläinen
 
RichFaces - Testing on Mobile Devices
RichFaces - Testing on Mobile DevicesRichFaces - Testing on Mobile Devices
RichFaces - Testing on Mobile Devices
Pavol Pitoňák
 
Real World Lessons on the Pain Points of Node.js Applications
Real World Lessons on the Pain Points of Node.js ApplicationsReal World Lessons on the Pain Points of Node.js Applications
Real World Lessons on the Pain Points of Node.js Applications
Ben Hall
 
Vagrant + Rouster at salesforce.com - PuppetConf 2013
Vagrant + Rouster at salesforce.com - PuppetConf 2013Vagrant + Rouster at salesforce.com - PuppetConf 2013
Vagrant + Rouster at salesforce.com - PuppetConf 2013
Puppet
 
Unit testing in iOS featuring OCUnit, GHUnit & OCMock
Unit testing in iOS featuring OCUnit, GHUnit & OCMockUnit testing in iOS featuring OCUnit, GHUnit & OCMock
Unit testing in iOS featuring OCUnit, GHUnit & OCMockRobot Media
 
Adopt DevOps philosophy on your Symfony projects (Symfony Live 2011)
Adopt DevOps philosophy on your Symfony projects (Symfony Live 2011)Adopt DevOps philosophy on your Symfony projects (Symfony Live 2011)
Adopt DevOps philosophy on your Symfony projects (Symfony Live 2011)
Fabrice Bernhard
 
The JavaFX Ecosystem
The JavaFX EcosystemThe JavaFX Ecosystem
The JavaFX Ecosystem
Andres Almiray
 
Test Driven Development with JavaFX
Test Driven Development with JavaFXTest Driven Development with JavaFX
Test Driven Development with JavaFX
Hendrik Ebbers
 
Making the most of your Test Suite
Making the most of your Test SuiteMaking the most of your Test Suite
Making the most of your Test Suiteericholscher
 
Getting started with spfx
Getting started with spfxGetting started with spfx
Getting started with spfx
Jenkins NS
 
Vagrant+Rouster at salesforce.com
Vagrant+Rouster at salesforce.comVagrant+Rouster at salesforce.com
Vagrant+Rouster at salesforce.com
chorankates
 
SmokeTests
SmokeTestsSmokeTests
Workshop quality assurance for php projects tek12
Workshop quality assurance for php projects tek12Workshop quality assurance for php projects tek12
Workshop quality assurance for php projects tek12
Michelangelo van Dam
 
Leveling Up With Unit Testing - LonghornPHP 2022
Leveling Up With Unit Testing - LonghornPHP 2022Leveling Up With Unit Testing - LonghornPHP 2022
Leveling Up With Unit Testing - LonghornPHP 2022
Mark Niebergall
 
How to test infrastructure code: automated testing for Terraform, Kubernetes,...
How to test infrastructure code: automated testing for Terraform, Kubernetes,...How to test infrastructure code: automated testing for Terraform, Kubernetes,...
How to test infrastructure code: automated testing for Terraform, Kubernetes,...
Yevgeniy Brikman
 
Introduction to PowerShell
Introduction to PowerShellIntroduction to PowerShell
Introduction to PowerShell
Boulos Dib
 

Similar to Create, test, secure, repeat (20)

Fighting Fear-Driven-Development With PHPUnit
Fighting Fear-Driven-Development With PHPUnitFighting Fear-Driven-Development With PHPUnit
Fighting Fear-Driven-Development With PHPUnit
 
Testing NodeJS with Mocha, Should, Sinon, and JSCoverage
Testing NodeJS with Mocha, Should, Sinon, and JSCoverageTesting NodeJS with Mocha, Should, Sinon, and JSCoverage
Testing NodeJS with Mocha, Should, Sinon, and JSCoverage
 
Leveling Up With Unit Testing - php[tek] 2023
Leveling Up With Unit Testing - php[tek] 2023Leveling Up With Unit Testing - php[tek] 2023
Leveling Up With Unit Testing - php[tek] 2023
 
Performance and Scalability Testing with Python and Multi-Mechanize
Performance and Scalability Testing with Python and Multi-MechanizePerformance and Scalability Testing with Python and Multi-Mechanize
Performance and Scalability Testing with Python and Multi-Mechanize
 
Automatic testing and quality assurance for WordPress plugins and themes
Automatic testing and quality assurance for WordPress plugins and themesAutomatic testing and quality assurance for WordPress plugins and themes
Automatic testing and quality assurance for WordPress plugins and themes
 
RichFaces - Testing on Mobile Devices
RichFaces - Testing on Mobile DevicesRichFaces - Testing on Mobile Devices
RichFaces - Testing on Mobile Devices
 
Real World Lessons on the Pain Points of Node.js Applications
Real World Lessons on the Pain Points of Node.js ApplicationsReal World Lessons on the Pain Points of Node.js Applications
Real World Lessons on the Pain Points of Node.js Applications
 
Vagrant + Rouster at salesforce.com - PuppetConf 2013
Vagrant + Rouster at salesforce.com - PuppetConf 2013Vagrant + Rouster at salesforce.com - PuppetConf 2013
Vagrant + Rouster at salesforce.com - PuppetConf 2013
 
Unit testing in iOS featuring OCUnit, GHUnit & OCMock
Unit testing in iOS featuring OCUnit, GHUnit & OCMockUnit testing in iOS featuring OCUnit, GHUnit & OCMock
Unit testing in iOS featuring OCUnit, GHUnit & OCMock
 
Adopt DevOps philosophy on your Symfony projects (Symfony Live 2011)
Adopt DevOps philosophy on your Symfony projects (Symfony Live 2011)Adopt DevOps philosophy on your Symfony projects (Symfony Live 2011)
Adopt DevOps philosophy on your Symfony projects (Symfony Live 2011)
 
The JavaFX Ecosystem
The JavaFX EcosystemThe JavaFX Ecosystem
The JavaFX Ecosystem
 
Test Driven Development with JavaFX
Test Driven Development with JavaFXTest Driven Development with JavaFX
Test Driven Development with JavaFX
 
Making the most of your Test Suite
Making the most of your Test SuiteMaking the most of your Test Suite
Making the most of your Test Suite
 
Getting started with spfx
Getting started with spfxGetting started with spfx
Getting started with spfx
 
Vagrant+Rouster at salesforce.com
Vagrant+Rouster at salesforce.comVagrant+Rouster at salesforce.com
Vagrant+Rouster at salesforce.com
 
SmokeTests
SmokeTestsSmokeTests
SmokeTests
 
Workshop quality assurance for php projects tek12
Workshop quality assurance for php projects tek12Workshop quality assurance for php projects tek12
Workshop quality assurance for php projects tek12
 
Leveling Up With Unit Testing - LonghornPHP 2022
Leveling Up With Unit Testing - LonghornPHP 2022Leveling Up With Unit Testing - LonghornPHP 2022
Leveling Up With Unit Testing - LonghornPHP 2022
 
How to test infrastructure code: automated testing for Terraform, Kubernetes,...
How to test infrastructure code: automated testing for Terraform, Kubernetes,...How to test infrastructure code: automated testing for Terraform, Kubernetes,...
How to test infrastructure code: automated testing for Terraform, Kubernetes,...
 
Introduction to PowerShell
Introduction to PowerShellIntroduction to PowerShell
Introduction to PowerShell
 

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 default
Michelangelo 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 functions
Michelangelo van Dam
 
Privacy by design
Privacy by designPrivacy by design
Privacy by design
Michelangelo van Dam
 
DevOps or DevSecOps
DevOps or DevSecOpsDevOps or DevSecOps
DevOps or DevSecOps
Michelangelo van Dam
 
Privacy by design
Privacy by designPrivacy by design
Privacy by design
Michelangelo van Dam
 
Continuous deployment 2.0
Continuous deployment 2.0Continuous deployment 2.0
Continuous deployment 2.0
Michelangelo van Dam
 
Let your tests drive your code
Let your tests drive your codeLet your tests drive your code
Let your tests drive your code
Michelangelo 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 story
Michelangelo 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 advantage
Michelangelo van Dam
 
The road to php 7.1
The road to php 7.1The road to php 7.1
The road to php 7.1
Michelangelo van Dam
 
Open source for a successful business
Open source for a successful businessOpen source for a successful business
Open source for a successful business
Michelangelo 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 later
Michelangelo 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 minutes
Michelangelo 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 heaven
Michelangelo van Dam
 
Getting hands dirty with php7
Getting hands dirty with php7Getting hands dirty with php7
Getting hands dirty with php7
Michelangelo 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
 
The Continuous PHP Pipeline
The Continuous PHP PipelineThe Continuous PHP Pipeline
The Continuous PHP Pipeline
Michelangelo 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 tests
Michelangelo 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 api
Michelangelo van Dam
 
Your code are my tests
Your code are my testsYour code are my tests
Your code are my tests
Michelangelo 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
 
Your code are my tests
Your code are my testsYour code are my tests
Your code are my tests
 

Recently uploaded

H.Seo, ICLR 2024, MLILAB, KAIST AI.pdf
H.Seo,  ICLR 2024, MLILAB,  KAIST AI.pdfH.Seo,  ICLR 2024, MLILAB,  KAIST AI.pdf
H.Seo, ICLR 2024, MLILAB, KAIST AI.pdf
MLILAB
 
LIGA(E)11111111111111111111111111111111111111111.ppt
LIGA(E)11111111111111111111111111111111111111111.pptLIGA(E)11111111111111111111111111111111111111111.ppt
LIGA(E)11111111111111111111111111111111111111111.ppt
ssuser9bd3ba
 
CFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptx
CFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptxCFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptx
CFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptx
R&R Consult
 
Architectural Portfolio Sean Lockwood
Architectural Portfolio Sean LockwoodArchitectural Portfolio Sean Lockwood
Architectural Portfolio Sean Lockwood
seandesed
 
Final project report on grocery store management system..pdf
Final project report on grocery store management system..pdfFinal project report on grocery store management system..pdf
Final project report on grocery store management system..pdf
Kamal Acharya
 
Halogenation process of chemical process industries
Halogenation process of chemical process industriesHalogenation process of chemical process industries
Halogenation process of chemical process industries
MuhammadTufail242431
 
Forklift Classes Overview by Intella Parts
Forklift Classes Overview by Intella PartsForklift Classes Overview by Intella Parts
Forklift Classes Overview by Intella Parts
Intella Parts
 
Sachpazis:Terzaghi Bearing Capacity Estimation in simple terms with Calculati...
Sachpazis:Terzaghi Bearing Capacity Estimation in simple terms with Calculati...Sachpazis:Terzaghi Bearing Capacity Estimation in simple terms with Calculati...
Sachpazis:Terzaghi Bearing Capacity Estimation in simple terms with Calculati...
Dr.Costas Sachpazis
 
Quality defects in TMT Bars, Possible causes and Potential Solutions.
Quality defects in TMT Bars, Possible causes and Potential Solutions.Quality defects in TMT Bars, Possible causes and Potential Solutions.
Quality defects in TMT Bars, Possible causes and Potential Solutions.
PrashantGoswami42
 
CME397 Surface Engineering- Professional Elective
CME397 Surface Engineering- Professional ElectiveCME397 Surface Engineering- Professional Elective
CME397 Surface Engineering- Professional Elective
karthi keyan
 
Automobile Management System Project Report.pdf
Automobile Management System Project Report.pdfAutomobile Management System Project Report.pdf
Automobile Management System Project Report.pdf
Kamal Acharya
 
The Benefits and Techniques of Trenchless Pipe Repair.pdf
The Benefits and Techniques of Trenchless Pipe Repair.pdfThe Benefits and Techniques of Trenchless Pipe Repair.pdf
The Benefits and Techniques of Trenchless Pipe Repair.pdf
Pipe Restoration Solutions
 
WATER CRISIS and its solutions-pptx 1234
WATER CRISIS and its solutions-pptx 1234WATER CRISIS and its solutions-pptx 1234
WATER CRISIS and its solutions-pptx 1234
AafreenAbuthahir2
 
road safety engineering r s e unit 3.pdf
road safety engineering  r s e unit 3.pdfroad safety engineering  r s e unit 3.pdf
road safety engineering r s e unit 3.pdf
VENKATESHvenky89705
 
在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样
在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样
在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样
obonagu
 
HYDROPOWER - Hydroelectric power generation
HYDROPOWER - Hydroelectric power generationHYDROPOWER - Hydroelectric power generation
HYDROPOWER - Hydroelectric power generation
Robbie Edward Sayers
 
Event Management System Vb Net Project Report.pdf
Event Management System Vb Net  Project Report.pdfEvent Management System Vb Net  Project Report.pdf
Event Management System Vb Net Project Report.pdf
Kamal Acharya
 
Gen AI Study Jams _ For the GDSC Leads in India.pdf
Gen AI Study Jams _ For the GDSC Leads in India.pdfGen AI Study Jams _ For the GDSC Leads in India.pdf
Gen AI Study Jams _ For the GDSC Leads in India.pdf
gdsczhcet
 
Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...
Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...
Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...
AJAYKUMARPUND1
 
Railway Signalling Principles Edition 3.pdf
Railway Signalling Principles Edition 3.pdfRailway Signalling Principles Edition 3.pdf
Railway Signalling Principles Edition 3.pdf
TeeVichai
 

Recently uploaded (20)

H.Seo, ICLR 2024, MLILAB, KAIST AI.pdf
H.Seo,  ICLR 2024, MLILAB,  KAIST AI.pdfH.Seo,  ICLR 2024, MLILAB,  KAIST AI.pdf
H.Seo, ICLR 2024, MLILAB, KAIST AI.pdf
 
LIGA(E)11111111111111111111111111111111111111111.ppt
LIGA(E)11111111111111111111111111111111111111111.pptLIGA(E)11111111111111111111111111111111111111111.ppt
LIGA(E)11111111111111111111111111111111111111111.ppt
 
CFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptx
CFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptxCFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptx
CFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptx
 
Architectural Portfolio Sean Lockwood
Architectural Portfolio Sean LockwoodArchitectural Portfolio Sean Lockwood
Architectural Portfolio Sean Lockwood
 
Final project report on grocery store management system..pdf
Final project report on grocery store management system..pdfFinal project report on grocery store management system..pdf
Final project report on grocery store management system..pdf
 
Halogenation process of chemical process industries
Halogenation process of chemical process industriesHalogenation process of chemical process industries
Halogenation process of chemical process industries
 
Forklift Classes Overview by Intella Parts
Forklift Classes Overview by Intella PartsForklift Classes Overview by Intella Parts
Forklift Classes Overview by Intella Parts
 
Sachpazis:Terzaghi Bearing Capacity Estimation in simple terms with Calculati...
Sachpazis:Terzaghi Bearing Capacity Estimation in simple terms with Calculati...Sachpazis:Terzaghi Bearing Capacity Estimation in simple terms with Calculati...
Sachpazis:Terzaghi Bearing Capacity Estimation in simple terms with Calculati...
 
Quality defects in TMT Bars, Possible causes and Potential Solutions.
Quality defects in TMT Bars, Possible causes and Potential Solutions.Quality defects in TMT Bars, Possible causes and Potential Solutions.
Quality defects in TMT Bars, Possible causes and Potential Solutions.
 
CME397 Surface Engineering- Professional Elective
CME397 Surface Engineering- Professional ElectiveCME397 Surface Engineering- Professional Elective
CME397 Surface Engineering- Professional Elective
 
Automobile Management System Project Report.pdf
Automobile Management System Project Report.pdfAutomobile Management System Project Report.pdf
Automobile Management System Project Report.pdf
 
The Benefits and Techniques of Trenchless Pipe Repair.pdf
The Benefits and Techniques of Trenchless Pipe Repair.pdfThe Benefits and Techniques of Trenchless Pipe Repair.pdf
The Benefits and Techniques of Trenchless Pipe Repair.pdf
 
WATER CRISIS and its solutions-pptx 1234
WATER CRISIS and its solutions-pptx 1234WATER CRISIS and its solutions-pptx 1234
WATER CRISIS and its solutions-pptx 1234
 
road safety engineering r s e unit 3.pdf
road safety engineering  r s e unit 3.pdfroad safety engineering  r s e unit 3.pdf
road safety engineering r s e unit 3.pdf
 
在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样
在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样
在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样
 
HYDROPOWER - Hydroelectric power generation
HYDROPOWER - Hydroelectric power generationHYDROPOWER - Hydroelectric power generation
HYDROPOWER - Hydroelectric power generation
 
Event Management System Vb Net Project Report.pdf
Event Management System Vb Net  Project Report.pdfEvent Management System Vb Net  Project Report.pdf
Event Management System Vb Net Project Report.pdf
 
Gen AI Study Jams _ For the GDSC Leads in India.pdf
Gen AI Study Jams _ For the GDSC Leads in India.pdfGen AI Study Jams _ For the GDSC Leads in India.pdf
Gen AI Study Jams _ For the GDSC Leads in India.pdf
 
Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...
Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...
Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...
 
Railway Signalling Principles Edition 3.pdf
Railway Signalling Principles Edition 3.pdfRailway Signalling Principles Edition 3.pdf
Railway Signalling Principles Edition 3.pdf
 

Create, test, secure, repeat