Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Testing + Laravel

Testing + Laravel

Christopher Pitt

September 30, 2014
Tweet

More Decks by Christopher Pitt

Other Decks in Programming

Transcript

  1. Automated testing can help to... → save you time in

    manual testing → remember everything you can't → document how you intend the code to function → protect you and your company
  2. Test-Driven development /** * @test */ public function itAdds() {

    $calculator = new Calculator(); $this->assertEquals( 4, $calculator->add(2, 2) ); }
  3. Test-Driven development ❯ phpunit PHPUnit 4.2.6 by Sebastian Bergmann. Configuration

    read from /path/to/phpunit.xml PHP Fatal error: Class 'Calculator' not found...
  4. Test-Driven development ❯ phpunit PHPUnit 4.2.6 by Sebastian Bergmann. Configuration

    read from /path/to/phpunit.xml . Time: 144 ms, Memory: 1.75Mb OK (1 test, 1 assertion)
  5. Test-Driven development class CalculatorTest extends PHPUnit_Framework_TestCase { /** * @test

    */ public function itAdds() { $calculator = new Calculator(); $this->assertEquals( 3, $calculator->add(1, 2) ); } }
  6. Test-Driven development ❯ phpunit PHPUnit 4.2.6 by Sebastian Bergmann. Configuration

    read from /path/to/phpunit.xml F Time: 57 ms, Memory: 1.75Mb There was 1 failure...
  7. Test-Driven development ❯ phpunit PHPUnit 4.2.6 by Sebastian Bergmann. Configuration

    read from /path/to/phpunit.xml . Time: 144 ms, Memory: 1.75Mb OK (1 test, 1 assertion)
  8. Test Coverage <?xml version="1.0" encoding="UTF-8"?> <phpunit bootstrap="vendor/autoload.php"> <testsuites> <testsuite> <directory>tests</directory>

    </testsuite> </testsuites> <filter> <whitelist addUncoveredFilesFromWhitelist="true"> <directory suffix=".php">source</directory> </whitelist> </filter> </phpunit>
  9. Test Coverage ❯ phpunit --coverage-html=report PHPUnit 4.2.6 by Sebastian Bergmann.

    Configuration read from /path/to/phpunit.xml . Time: 144 ms, Memory: 1.75Mb OK (1 test, 1 assertion) Generating code coverage report in HTML format ... done
  10. Continuous Integration language: php php: - 5.5 - 5.6 before_script:

    - composer install --no-interaction --prefer-dist
  11. SOLID → Single Responsibility interface Basket { public function getItems();

    public function addItem($item); public function removeItem($item); public function calculateTotal(); public function processPayment($account, $amount); }
  12. SOLID → Single Responsibility interface BasketCollection { public function getItems();

    public function addItem($item); public function removeItem($item); } interface BasketPaymentProcessor { public function calculateTotal(BasketCollection $collection); public function processPayment($account, $amount); }
  13. SOLID → Open-Closed you should be able to extend a

    classes behavior, without modifying it
  14. SOLID → Open-Closed class BasketPaymentProcessor { public function processPayment($account, $amount,

    $adapter) { switch ($adapter) { case "paypal": $this->chargeWithPaypal($account, $amount); break; case "paygate": $this->chargeWithPaygate($account, $amount); break; } } }
  15. SOLID → Open-Closed interface Adapter { public function charge($account, $amount);

    } class BasketPaymentProcessor { protected $adapters = []; public function addAdapter($key, Adapter $adapter) { $this->adapters[$key] = $adapter; } }
  16. SOLID → Lizkov Substitution class ProductSorter { public function sort(array

    $products) { usort($products, function($a, $b) { if ($a->name == $b->name) { return 0; } return ($a->name < $b->name) ? -1 : 1; }); return $products; } }
  17. SOLID → Lizkov Substitution class EnhancedProductSorter extends ProductSorter { public

    function sort(array $products) { foreach ($products as $product) { if (!($product instanceof EnhancedProduct)) { throw new InvalidArgumentException(); } } return parent::sort($products); } }
  18. SOLID → Interface Segregation clients should not be forced to

    depend upon interfaces that they do not use
  19. SOLID → Interface Segregation interface CacheInterface { public function connect($host,

    $username, $password, $schema); public function set($key, $value); public function get($key); }
  20. SOLID → Interface Segregation interface ConnectionInterface { public function connect();

    } interface CacheInterface { public function set($key, $value); public function get($key); }
  21. SOLID → Dependency Inversion class Logger { public function write($message)

    { file_put_contents(__DIR__ . "/application.log", $message); } }
  22. SOLID → Dependency Inversion class Logger { protected $writer; public

    function __construct(Writer $writer) { $this->writer = $writer; } public function write($message) { $this->writer->write($message); } }
  23. Using interfaces class HomeController extends Controller { protected $products; public

    function __construct(EloquentProductRepository $products) { $this->products = $products; } public function welcome() { $products = $this->products->all(); return View::make("home/welcome", compact("products")); } }
  24. Using interfaces interface ProductRepository { public function all(); } class

    HomeController extends Controller { protected $products; public function __construct(ProductRepository $products) { $this->products = $products; } }
  25. Using interfaces use Illuminate\Support\ServiceProvider; class ProductServiceProvider extends ServiceProvider { public

    function register() { $this->app->bind( ProductRepository::class, EloquentProductRepository::class ); } }
  26. Reducing Controllers class ProductController extends Controller { public function add()

    { $validator = Validator::make(Input::all(), $this->rules); if ($validator->fail()) { return Redirect::back()->withErrors($validator)->withInput(); } $product = Product::create(Input::all()); return Redirect::route("product/index"); } }
  27. Reducing Controllers class ProductService { protected $validator; protected $repository; public

    function __construct(Validator $validator, Repository $repository) { $this->validator = $validator; $this->repository = $repository; } }
  28. Reducing Controllers public function add(Responder $responder, array $data) { if

    (!$this->validator->passes($data, $this->rules)) { return $responder->errors($this->validator->errors()); } return $responder->ok($this->repository->add($data)); }
  29. Reducing Controllers class ProductController extends Controller implements Responder { protected

    $service; public function __construct(ProductService $service) { $this->service = $service; } }
  30. Reducing Controllers public function add() { return $this->service->add($this, Input::all()); }

    public function errors($errors) { return Redirect::back()->withErrors($errors)->withInput(); } public function ok(array $product) { return Redirect::route("product/index"); }
  31. Using repositories primedia/Repository/ |-- ArticleRepository.php |-- ... `-- EloquentRepository/ |--

    ArticleEloquentRepository.php |-- ... |-- Model/ `-- Transformer/ 10 directories, 177 files