Writing Testable Code

Post on 22-Dec-2014

84 views 3 download

Tags:

description

 

Transcript of Writing Testable Code

Writing Testable Code

“Testable”?• When we write object oriented code, we write individual units

(classes / objects and their methods)

• Testable code is code that we can easily write automated unit tests for

• Testable code is of a better quality, more isolated and written to comply with SOLID* principles

• This is what we will work towards* more on this later

Types of Automated Tests

• Unit tests - a test that verifies the behaviour an individual method, function or class / object

• Functional tests - tests that ensure the application does what it is supposed to without caring about how it achieves it

• Behavioural testing - verifies that the software behaves as the user would expect it to - usually involves automating the browser

Why are tests so important?

!4

• When we write code, how do we know it behaves as we expect?

• If we write some code that performs the addition of two numbers, how do we know it handles negative values correctly?

• We can manually test our code, but this isn’t good enough

• As programmers we should always look to automate our processes to reduce repetition, tests are no exception to this rule.

Benefits of Automated Testing• Tests prove that our code works as we expect

• Writing our tests makes us think about edge cases, and what we expect from our code in those cases

• Protects against regressions

• Let’s us know that something is broken before we ship a release

• Reduces the amount of manual testing required

RefactoringAutomated tests allow us to refactor with confidence

Tests + Continuous Integration• We currently use Jenkins as our continuous integration server

(https://jenkins.dt.awsripple.com)

• Jenkins “builds” our project and let’s us know if it’s broken

• If we have tests that cover every business rule in our application, we will know the code is broken before we ship a release

• Reduces the feedback loop between us and the client

• Improves quality

Thinking About Dependencies

What is a dependency?• When we write multiple units of code (multiple classes / objects),

they work together to create a working application / website.

• We refer to these units as components

• A component might rely on another component in order to work

• If component X relies on component Y, we say that component X has a dependency on component Y

Mandatory Dependencies

• A mandatory dependency is something that the component cannot function without

• For example, we could say that a smart phone has a mandatory dependency on an operating system

• When a dependency is mandatory, we inject it into the constructor of the dependent object

Mandatory Dependencies<?php!! !class SamsungGalaxy extends Phone!{!! private $operatingSystem;!!

! public function __construct(OperatingSystem $android)!! {!! ! $this->operatingSystem = $android;!! }!}

Optional Dependencies

• An optional dependency is something that the component can function without

• For example, we could say that the smart phone optionally depends on a USB connection to a computer

• When a dependency is optional, we inject it via a setter method

Optional Dependencies<?php!! !class SamsungGalaxy extends Phone!{!! private $operatingSystem;!! private $usbConnection;!!! public function __construct(OperatingSystem $android)!! {!! ! $this->operatingSystem = $android;!! }!!! public function setUsbConnection(UsbConnection $usbConnection)!! {!! ! $this->usbConnection = $usbConnection;!! }!}

Optional Dependencies<?php!! !class SamsungGalaxy extends Phone!{!! // ...!!! public function receiveCall(PhoneCall $call)!! {!! ! if (null !== $this->usbConnection) {!! ! ! $this->usbConnection->haltTransfers();!! ! }!! !! ! $call->answer();! }!}

Why are objects dependent on another?

• In object oriented programming, we use objects (created from classes) to encapsulate functionality

• Each object should do something specific, and do it well

• This is known as Single Responsibility Principle (SRP) - the S in the SOLID principles (we will cover more of these over the next few sessions)

Example of SRP• Earlier we talked about injecting an OperatingSystem object into

the SamsungGalaxy phone object

• This is a separation of responsibility, because the phone is not implementing the logic of the operating system

• If we had all of our logic of the OperatingSystem object inside the SamsungGalaxy object, it would be doing too much and would violate SRP

• This would allow us to test our OperatingSystem as a unit of code

Real Code Example

Code Example: User Manager• Let’s say we have a bunch of users in an application

• We have an object in our application that is responsible for managing users, the UserManager

• The UserManager is where we create, update and delete users in the database

• We should create multiple components to ease the UserManager’s job

Code Example: User Manager

• Our manager needs to:

• Persist / update users to the database

• Hash passwords for users

• Delete users from the database

Code Example: User Manager<?php!! !class UserManager extends Phone!{!! private $db;!! private $passwordHasher!!! public function __construct(!! ! ConnectionInterface $db,!! ! PasswordHasherInterface $passwordHasher!! ) {!! ! $this->db = $db;!! ! $this->passwordHasher = $passwordHasher;! }!}

Code Example: User Manager• With separate components, we can write tests for each of them in

isolation

• We can also swap our dependencies out easily if we choose to do so, our UserManager won’t care

• When writing our tests for the UserManager we can mock* any dependencies (e.g. the DatabaseConnectionInterface) which means we don’t need to test with real dependencies

• Mocking allows us to test units of code on their own, rather than doing integration testing (hence the term “Unit Testing”)

*more on mocking in a future session

Step 2: Managing Dependencies

Objects With Dependencies

• Separating concerns into different objects means we have to create multiple objects

• This can get unwieldy when we have several dependencies

• This also means that our code has to be aware of all the different dependencies that an object relies on….

Example: Inline Instantiation<?php!! !class UserController!{!! public function createAction()!! {!! ! $passwordHasher = new BcryptPasswordHasher();!! ! $connection = new DatabaseConnection($options);!!! ! $userManager = new UserManager($connection, $passwordHasher);!! }!}

This is a nightmare…

Dependency Injection Containers (DIC)

DIC: Managing Dependencies

• In our UserController example, we needed to have knowledge of the dependencies that the UserManager required

• What if we wanted to change the BcryptPasswordHasher to PbkPasswordHasher?

• We would have to change code all over our application (wherever we have used the UserManager)

DIC: Managing Dependencies

• A DIC will manage our objects (sometimes referred to as services) and their dependencies for us

• If we want to get our UserManager from a DIC, we just need to ask for it - we don’t care what else the UserManager depends on

• This allows us to scale the complexity of our object dependencies without complicating our code

Pimple: A simple DIC<?php!! !class Container extends \Pimple!{!! public function __construct()!! {!! ! $this[‘user_manager’] = function() {!! ! ! $passwordHasher = new BcryptPasswordHasher();!! ! ! $connection = new DatabaseConnection();!!! ! ! return new UserManager($passwordHasher, $connection);!! ! };!! }!}

Pimple: A simple DIC<?php!! !class Container extends \Pimple!{!! public function __construct()!! {!! ! $this[‘password_hasher’] = function() {!! ! ! return new BcryptPasswordHasher();!! ! };!!! ! $this[‘db’] = function() {!! ! ! return new DatabaseConnection();!! ! };!!! ! $this[‘user_manager’] = function($c) {!! ! ! return new UserManager($c[‘password_hasher’], $c[‘db’]);!! ! };! }!}

Even better…

Using the DIC

<?php!! !class UserController!{!! public function createAction()!! {!! ! $container = $this->getContainer(); // fetch the container!! ! $userManager = $container[‘user_manager’];!! }!}

• Our controller action is now much simpler and has no knowledge of the UserManager’s dependencies.

What Next?

• Pimple can be implemented on any legacy project, just install it using composer

• We can start separating concerns when writing our code, always think about SRP

• Read about the SOLID principles and understand them (ask for help if needed)

Writing Unit TestsNext Session: