-
Notifications
You must be signed in to change notification settings - Fork 1
Unit Test: Test Doubles
These exercises give an introduction to unit tests in Magento and test doubles.
This exercise is based on the contacts module, as developed in the Integration-Test: Controllers exercise. If
you did not complete it, clone https://github.com/tddwizard/magento2-exercise-contact.git into
app/code/TddWizard/ExerciseContact
and check out the branch solution-integration-test-5
.
The goal of this exercise is to refactor the existing solution to a thin controller and move the business logic in a new domain layer. We will create a service from scratch with TDD, then replace the code in the controller with delegation to the service. For anything non-trivial this should be how you start out in the first place.
Create a new class: Service\CreateInquiry
with a method createFromInput(string message, string email = null)
For the first exercise, only implement that it saves an inquiry with message and email if the input is valid, and throw an exception if it is not valid.
The class will only need two dependencies: InquiryRepositoryInterface
and InquiryInterfaceFactory
. Their real
implementations cannot be used - we do not have a database in unit tests!
So, for tests, create a fake implementation of the repository that saves the inquiries in memory. For this test, only
the save()
method actually needs to be implemented, the others can stay empty.
Example:
class InquiryMemoryRepository implements InquiryRepositoryInterface
{
public $inquiries = [];
public function save(InquiryInterface $inquiry)
{
$this->inquiries[] = $inquiry;
}
//...
}
By making $inquiries
public, you can easily inspect the saved inquiries from the test.
The (generated) InquiryInterfaceFactory
does not have an interface so that we cannot write our own implementation.
We need a stub, that PHPUnit can generate for us with $this->createMock(InqiuryInterfaceFactory::class)
, if the
class has been generated. The Magento unit testing framework takes care of generation. For the Inquiry model that we
let the factory return, we can use $this->createPartialMock(Inquiry::class)
, this way the getter and setter methods
still work and just the dependencies are stubbed.
Check out solution-unit-test-1
to see a solution.
Now the email address should come from the customer if they are logged in.
The service needs a new dependency: Customer\Session
. This class does not have an interface, so it's not so easy to
replace. Use the PHPUnit mock feature to create a stub (mock without expectations).
Check out solution-unit-test-2
to see a solution.
Finally, let the service also take care of success and error messages. It should not throw exceptions for invalid input anymore, but deliver error messages to the user.
The service needs a new dependency again: \Magento\Framework\Message\ManagerInterface
. A fake implementation similar
to exercise 1 would be possible, but try a different approach: a PHPUnit mock. Write expectations for
addErrorMessage
and addSuccessMessage
.
Check out solution-unit-test-3
to see a solution.
Finally, replace the previous code in the Save
controller. The integration tests should tell you if it was successful!
Check out solution-unit-test-integration
to see a solution.
- Hire me to come to your company: https://www.integer-net.com/services/magento-training-courses/#M2-test
- Subscribe to the Test Driven Magento newsletter to get notified about other possibilities: http://tddwizard.com/