How can we write code that is easy to test? This talk is not a complete reference, it just tries to list some practical advice to ease the process of getting into testing.
7. That wasn't so bad!
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
8. Current status:
A metric ton of untested legacy code
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
9. Start with writing tests for bugs
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
10. THIS IS HARD
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
11. This costs lots of time!
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
12. This is painful!
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
13. There has to be
a better way...!
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
14. <?php
/**
* @todo make more testable
*
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
15. Things to keep in mind
to write TESTABLE CODE
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
17. A bad example:
Event Observer
(for Magento 1)
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
18. <?php
use Varien_Event_Observer as Event;
class Netzarbeiter_CustomerActivation_Model_Observer
{
// Check if the customer has been activated, if not, throw login error
public function customerLogin(Event $event) {...}
// Flag new accounts as such
public function customerSaveBefore(Event $event) {...}
// Send out emails
public function customerSaveAfter(Event $event) {...}
// Abort registration during checkout if default activation status is false
public function salesConvertQuoteAddressToOrder(Event $event) {...}
// Add customer activation option to the mass action block
public function adminhtmlBlockHtmlBefore(Event $event) {...}
// Add the customer_activated attribute to the customer grid collection
public function eavCollectionAbstractLoadBefore(Event $event) {...}
// Add customer_activated column to CSV and XML exports
public function coreBlockAbstractPrepareLayoutAfter(Event $event) {...}
// Remove the customer id from the customer/session, in effect causing a logout just in case
public function controllerActionPostdispatchCustomerAccountResetPasswordPost(Event $event) {...}
}
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
24. <?php
use Varien_Event_Observer as Event;
class Netzarbeiter_CustomerActivation_Model_Observer_ProhibitInactiveLogin
{
// Check if the customer has been activated, if not, throw login error
public function customerLogin(Event $event) {...}
// Abort registration during checkout if default activation status is false
public function salesConvertQuoteAddressToOrder(Event $event) {...}
// Remove the customer id from the customer/session, in effect causing a logout just in case
public function controllerActionPostdispatchCustomerAccountResetPasswordPost(Event $event) {...}
}
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
25. <?php
use Varien_Event_Observer as Event;
class Netzarbeiter_CustomerActivation_Model_Observer_EmailNotifications
{
// Flag new accounts as such
public function customerSaveBefore(Event $event) {...}
// Send out emails
public function customerSaveAfter(Event $event) {...}
}
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
26. <?php
use Varien_Event_Observer as Event;
class Netzarbeiter_CustomerActivation_Model_Observer_AdminhtmlCustomerGrid
{
// Add customer activation option to the mass action block
public function adminhtmlBlockHtmlBefore(Event $event) {...}
// Add the customer_activated attribute to the customer grid collection
public function eavCollectionAbstractLoadBefore(Event $event) {...}
// Add customer_activated column to CSV and XML exports
public function coreBlockAbstractPrepareLayoutAfter(Event $event) {...}
}
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
41. ▸ Dependencies can be injected
▸ Business logic moved into model
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
42. Model with specific responsibility
class Netzarbeiter_CustomerActivation_Model_CustomerLoginSentry
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
43. public function abortLoginIfNotActive(Mage_Customer_Model_Customer $customer)
{
if (! $customer->getData('customer_activated') {
$this->getSession()->logout();
$this->getDisplay()->showLoginAbortedMessage();
}
}
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
44. ▸ Business logic independent of entry point
▸ More type safety
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
57. How far can delegation go?
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
58. Delegate until the next
delegator == delegatee
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
59. In the end
our class
is wrapping
a Magento class
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
60. In the end
our class
is wrapping
a Framework class
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
66. WHY DOES
THE CLASS EXIST?
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
67. WHY DOES
THE METHOD EXIST?
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
68. RETURN A VALUE
OR
SIDE EFFECT
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
69. If a method
RETURNS A VALUE
only test for that
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
70. If a method
CAUSES A SIDE EFFECT
only test for that
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
74. Side Effect #2:
A method call on another object
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
80. ▸ Separation between
Hooks or Entry Points
and
Business Logic
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
81. ▸ Split Business Logic into specific classes
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
82. ▸ Use IDCDD to find where to separate
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
83. ▸ Also separate code that
returns a value
from code that
causes a side effect
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
84. ▸ Test if a class fulfills it's purpose
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp
85. ▸ Don't use magic methods
(No more calls to __call())
Writing Testable Code - MageTitans Mini, 5th May 2016 - ! - contact@vinaikopp.com - twitter://@VinaiKopp