AspectMock
Making it easier to write unit tests
So, yeah, tests are important
“Test code is just as important as production code.”
“… tests enable change”“Code, without tests, is not clean. No matter how elegant it is, no
matter how readable and accessible, if it hath not tests, it be unclean.”
“All software you write will be tested – if not by you and
your team, then by the eventual users.”
“…legacy code is simply code without tests.”
“Code without tests is bad code. … With tests we can change the
behavior of our code quickly and verifiably. Without them, we
really don’t know if our code is getting better or worse.”
From Clean Code by Robert Martin, The Pragmatic Programmer by Andrew Hunt and David Thomas, and Working Effectively With Legacy Code by Michael Feathers
If you have trivial methods, tests are easy
Code Test
But who has trivial methods?
“They” say use Dependency Injection and mocks
Changing the
method’s
signature requires
refactoring all
method calls.
DI does nothing
for static
method calls
https://phpunit.de/manual/current/en/test-doubles.html
Or you could write integration tests …
Code Test
Even with integration tests, this
path cannot be tested because it
is a time-sensitive conditional
That’s a lot of work
Is there a third option?
A. Refactor all of your code to use Dependency Injections so you can mock dependencies.
B. Write intricate integration tests with heavy setup and teardown
C. ??
C. Use AspectMock
• Allows you to mock any method call (static, private, built-in PHP)
• Does not require refactoring existing code
• Installs via Composer (and configure an autoloader for tests)
• Can return a different mock value for each individual test
• Easy to use
• Magical
AspectMock mocks your code as it is
Code Test
Here’s the basic setup
• Import AspectMockTest
• In tearDown() method, run clean() to
reset the mocks between tests
• Define the class to be mocked, and the
desired results for each of its methods
• Write your test
Code
Want a different
value for each
test? Okay
Test
RealSample
TestCodeRealSample
So, how does it work?
• Relies on Aspect Oriented Programming
• Some concerns cut across classes (security, logging, etc.)
• Identify join points (method invocation, method return)
• Write code that provides advice at designated join points
• AOP code can target methods without cluttering those methods
• Uses PHP implementation of AOP called Go! AOP Framework
(http://go.aopphp.com)
Setup
1. Install via Composer
Be sure to include any path
you may mock, and exclude
all test-related paths
2. Create custom autoloader
3. Point phpunit to
special autoloader
Ready to try it for yourself?
Website: https://github.com/Codeception/AspectMock

AspectMock

  • 1.
    AspectMock Making it easierto write unit tests
  • 2.
    So, yeah, testsare important “Test code is just as important as production code.” “… tests enable change”“Code, without tests, is not clean. No matter how elegant it is, no matter how readable and accessible, if it hath not tests, it be unclean.” “All software you write will be tested – if not by you and your team, then by the eventual users.” “…legacy code is simply code without tests.” “Code without tests is bad code. … With tests we can change the behavior of our code quickly and verifiably. Without them, we really don’t know if our code is getting better or worse.” From Clean Code by Robert Martin, The Pragmatic Programmer by Andrew Hunt and David Thomas, and Working Effectively With Legacy Code by Michael Feathers
  • 3.
    If you havetrivial methods, tests are easy Code Test
  • 4.
    But who hastrivial methods?
  • 5.
    “They” say useDependency Injection and mocks Changing the method’s signature requires refactoring all method calls. DI does nothing for static method calls https://phpunit.de/manual/current/en/test-doubles.html
  • 6.
    Or you couldwrite integration tests … Code Test Even with integration tests, this path cannot be tested because it is a time-sensitive conditional That’s a lot of work
  • 7.
    Is there athird option? A. Refactor all of your code to use Dependency Injections so you can mock dependencies. B. Write intricate integration tests with heavy setup and teardown C. ??
  • 8.
    C. Use AspectMock •Allows you to mock any method call (static, private, built-in PHP) • Does not require refactoring existing code • Installs via Composer (and configure an autoloader for tests) • Can return a different mock value for each individual test • Easy to use • Magical
  • 9.
    AspectMock mocks yourcode as it is Code Test
  • 10.
    Here’s the basicsetup • Import AspectMockTest • In tearDown() method, run clean() to reset the mocks between tests • Define the class to be mocked, and the desired results for each of its methods • Write your test
  • 11.
    Code Want a different valuefor each test? Okay Test
  • 12.
  • 13.
    So, how doesit work? • Relies on Aspect Oriented Programming • Some concerns cut across classes (security, logging, etc.) • Identify join points (method invocation, method return) • Write code that provides advice at designated join points • AOP code can target methods without cluttering those methods • Uses PHP implementation of AOP called Go! AOP Framework (http://go.aopphp.com)
  • 14.
    Setup 1. Install viaComposer Be sure to include any path you may mock, and exclude all test-related paths 2. Create custom autoloader 3. Point phpunit to special autoloader
  • 15.
    Ready to tryit for yourself? Website: https://github.com/Codeception/AspectMock

Editor's Notes

  • #4 Walk through both the code and the test on this
  • #5 Explain what the code is doing here. Then talk about how it’s hard to test.
  • #6 PHPUnit is designed to pass mocks through dependency injection. Injecting dependencies sometimes violates separate of concerns. The calling methods shouldn’t know what calculateTax needs to do its job.
  • #7 Set up what you need – a test User, location, and item. Use real TaxCode and Calendar classes, maybe with test data in those. It takes a lot of work to set up integration tests. If using dbase, tests are slower and dbase can get littered with test data from failed tests that don’t tear down correctly
  • #10 Step through test line by line, then walk through execution
  • #13 Combines integrations and mocks. Code is from a database mapper. The test is to determine if the dbase query runs correctly. We need real values in dbase due to FK constraints on ProjectId and SiteIds It uses values set up in the setUp function of the test, and removed in the teardown function In this sample, we are actually mocking the results of a private method within the method under test, because it relies on values we don’t want to set up. Remember, PHPUnit does not support mocking private methods, so we couldn’t do this in PHPUnit mocks.