Writing Testable Code

32
Writing Testable Code

description

 

Transcript of Writing Testable Code

Page 1: Writing Testable Code

Writing Testable Code

Page 2: 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

Page 3: Writing Testable Code

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

Page 4: Writing Testable Code

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.

Page 5: Writing Testable Code

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

Page 6: Writing Testable Code

RefactoringAutomated tests allow us to refactor with confidence

Page 7: Writing Testable Code

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

Page 8: Writing Testable Code

Thinking About Dependencies

Page 9: Writing Testable Code

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

Page 10: Writing Testable Code

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

Page 11: Writing Testable Code

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

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

Page 12: Writing Testable Code

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

Page 13: Writing Testable Code

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;!! }!}

Page 14: Writing Testable Code

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

Page 15: Writing Testable Code

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)

Page 16: Writing Testable Code

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

Page 17: Writing Testable Code

Real Code Example

Page 18: Writing Testable Code

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

Page 19: Writing Testable Code

Code Example: User Manager

• Our manager needs to:

• Persist / update users to the database

• Hash passwords for users

• Delete users from the database

Page 20: Writing Testable Code

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;! }!}

Page 21: Writing Testable Code

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

Page 22: Writing Testable Code

Step 2: Managing Dependencies

Page 23: Writing Testable Code

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….

Page 24: Writing Testable Code

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…

Page 25: Writing Testable Code

Dependency Injection Containers (DIC)

Page 26: Writing Testable Code

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)

Page 27: Writing Testable Code

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

Page 28: Writing Testable 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);!! ! };!! }!}

Page 29: Writing Testable Code

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…

Page 30: Writing Testable Code

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.

Page 31: Writing Testable Code

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)

Page 32: Writing Testable Code

Writing Unit TestsNext Session: