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 veriïŹes 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 - veriïŹes 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. BeneïŹts 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 speciïŹc, 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)