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