Your SlideShare is downloading. ×
Writing Testable Code
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Saving this for later?

Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime - even offline.

Text the download link to your phone

Standard text messaging rates apply

Writing Testable Code

97
views

Published on

Published in: Technology

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
97
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
2
Comments
0
Likes
0
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Writing Testable Code
  • 2. “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
  • 3. 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
  • 4. 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.
  • 5. 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
  • 6. Refactoring Automated tests allow us to refactor with confidence
  • 7. 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
  • 8. Thinking About Dependencies
  • 9. 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
  • 10. 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
  • 11. Mandatory Dependencies <?php! ! ! class SamsungGalaxy extends Phone! {! ! private $operatingSystem;! ! ! public function __construct(OperatingSystem $android)! ! {! ! ! $this->operatingSystem = $android;! ! }! }
  • 12. 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
  • 13. 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;! ! }! }
  • 14. Optional Dependencies <?php! ! ! class SamsungGalaxy extends Phone! {! ! // ...! ! ! public function receiveCall(PhoneCall $call)! ! {! ! ! if (null !== $this->usbConnection) {! ! ! ! $this->usbConnection->haltTransfers();! ! ! }! ! ! ! ! $call->answer();! }! }
  • 15. 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)
  • 16. 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
  • 17. Real Code Example
  • 18. 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
  • 19. Code Example: User Manager • Our manager needs to: • Persist / update users to the database • Hash passwords for users • Delete users from the database
  • 20. 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;! }! }
  • 21. 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
  • 22. Step 2: Managing Dependencies
  • 23. 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….
  • 24. 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…
  • 25. Dependency Injection Containers (DIC)
  • 26. 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)
  • 27. 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
  • 28. 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);! ! ! };! ! }! }
  • 29. 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…
  • 30. 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.
  • 31. 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)
  • 32. Writing Unit Tests Next Session: