testing with laravel
TRANSCRIPT
Designing for Testability with Laravel
Derek Binkley@DerekB_WI
About Me★Lead Developer at National Conference of
Bar Examiners★PHP and Java Developer★MySQL DBA★Father of Three★Second ever talk
About NCBEExamining Bars?
About NCBENo, developing the bar exam for future lawyers.
Plus, supporting state admission authorities.
What will we cover?
What is built in to Laravel for Unit TestingStrategies that make testing easier
Detailed examples
Unit Tests• Lowest level of testing• Typically done by developer• Crucial to Test Driven Design• Tests a Single Unit of Code and Verifies the
Results.
Why don’t developers test?• Think code will get replaced soon.• Think code is straightforward and won’t break.• Tests are hard to write.• Low perceived benefit.• Don’t understand how to design code for
testability.
Laravel and PHPUnit• PHPUnit Available by default
• Tests saved in Tests directory• Tests should extend TestCase class• Tests should be saved in file named *Test.php• Tests method names should begin with test.
What Does Laravel Provide?• Crawler – Gives us a mock browser/client to
run our controllers.
What Does Laravel Provide?• Additional assertions• ->assertResponseOk();• ->assertResponseStatus($code);• ->assertViewHas($key, $value = null);• ->assertViewHasAll(array $bindings);• ->assertViewMissing($key);• ->assertRedirectedTo($uri, $with = []);• ->assertRedirectedToRoute($name, $parameters = [], $with = []);• ->assertRedirectedToAction($name, $parameters = [], $with = []);• ->assertSessionHas($key, $value = null);• ->assertSessionHasAll(array $bindings);• ->assertSessionHasErrors($bindings = [], $format = null);• ->assertHasOldInput();
Faking Data• https://github.com/fzaninotto/Faker• Allows us to not spend time making up test
data.
Example Form
Example Controller
Test Code – First Try
Problems?• Database is not cleaned up. We could use
seeding in our setup and teardown methods.• Email attempt adds a dependency upon a
network or cloud service.• Testing all the logic of the controller at once
makes problems hard to diagnose.
Laravel Can Help
• Disabling Middleware allows testing only your controller code• Wrapping test in a database transaction helps clean up• Using migrations allows for resetting database to consistent
state.
How we can help ourselves• DRY – Don’t Repeat Yourself – Be Lazy, write code once• Single Responsibility Object – No “Jack of all Trades” Objects• Dependency Injection – Give me what I need to do my job• Keep cyclomatic complexity low – If there is a fork in the road
take it• Law of Demeter – Don’t extend reach too far• Design Patterns – Somebody has already solved our problems
Improving Our Example
• Remove dependency on session
Rerun Test and See Failure
• Remove if (is_numeric(session($user_id)))
Dependency Injection• Pass in the objects you need• Code to interfaces• Avoid creating a “new” object.• Setter Injection v. Constructor Injection• Helps follow Law of Demeter
Arguments Against DI• Our dependencies won’t change• Too complex
Benefits of DI• Can now mock objects while testing• Test our code, not 3rd party libraries• Loose coupling allows for easier upgrades• Built in to Laravel
Let’s try it out• Laravel will inject an instance of a class that is type hinted in a
constructor or method.
• Now we can use our factory anywhere within our controller.
Controller is Much Skinnier
Mock objects allow for easy controller testing
• We create a mock PersonFactory and specify which methods will be called and what they should return.
• Our person creation logic is now moved to a single class (DRY), our controller can now be tested without a database.
• Yet to test our person factory we must still seed and clean up our database.
Repository Pattern• Separate getting the data from the data
object. • Code to an interface to allow for mocking.
Wrapper Around Class• Takes class doing work as parameter• Easy to Mock• Won’t use underlying resources, e.g. API, to
run test.
Mocking a Facade• The Mail class is a Laravel façade so we can
easily mock it like this.
• Laravel takes care of the rest for us.
Resources• http://martinfowler.com/eaaCatalog/tableDataGateway.html• https://www.youtube.com/watch?v=HhwElTL-mdI - Evan Dorn LA Ruby Meetup• https://www.youtube.com/watch?v=X6c11niH-U8 - Chris Hartjes Ski PHP• https://bosnadev.com/2015/03/07/using-repository-pattern-in-laravel-5/• https://github.com/fzaninotto/Faker• http://laravelcoding.com/blog/laravel-5-beauty-testing• https://laravel.com/docs/5.1/testing• http://martinfowler.com/articles/mocksArentStubs.html
Thanks• Derek Binkley - @DerekB_WI or [email protected]• Slides are at
http://www.slideshare.net/DerekBinkley/testing-with-laravel
• Feedback on Joind.in at https://joind.in/talk/dc052