28
Magento code testability: Problems and Solutions

Magento code testability: Problems and Solutions

Embed Size (px)

Citation preview

Page 1: Magento code testability: Problems and Solutions

Magento code testability: Problems and Solutions

Page 2: Magento code testability: Problems and Solutions

Unit testing

One of the main reasons for unit testing is improvement of code quality.

Unit tests are indicators that instantly show all the defects of object oriented code.

If code is hard to test – its quality is questionable.

Page 3: Magento code testability: Problems and Solutions

Code flaws in magento detected by unit tests

• Fat constructors• Method complexity• Poor OOD (God objects)• Law of Demeter violations• Global State and Behavior usage• …

Page 4: Magento code testability: Problems and Solutions

Fat constructors

Objects that have a lot of behavior in constructors are hard to test.

You’ve just created the object and it already created other objects, made some global calls, changed some global state, etc.

Page 5: Magento code testability: Problems and Solutions

Fat constructors » Solution

Bad: mock all dependencies, create constructor tests and test all scenarios of constructor.

Good: Move all behavior from constructors. Leave only data initialization code.

Page 6: Magento code testability: Problems and Solutions

Method Complexity

Unit tests test behavior scenarios. Unit testing paradigm requires every scenario covered by separate test.

Each flow control statement adds scenario to method, so complex methods with many control structures and protected calls require a lot of tests and mocks/stubs.

Page 7: Magento code testability: Problems and Solutions

Method Complexity » Solution

Bad: For protected calls – use reflection or inheritance to test protected behavior in isolation. Write test per each scenario.

Good: Extract behavior from complex methods to separate objects that have small dependencies and are easily testable. Substitute conditions with polymorphism.

Page 8: Magento code testability: Problems and Solutions

Poor OOD (God objects)

If an object has too many responsibilities there is a big chance that it will have internal calls between its public methods.

This creates problems for testing. Developer has to mock whole object to stub internal public calls, otherwise he will have test duplication.

Page 9: Magento code testability: Problems and Solutions

Poor OOD (God objects) » Solution

Bad: Mock tested object and stub internal public calls.

Good: Extract small objects that will have their own responsibilities to avoid internal public calls.

Page 10: Magento code testability: Problems and Solutions

Law of Demeter Violations

When tested object receives some context object that is used only to gain access to third service object the Law of Demeter is violated.

To test such code one would have to stub context object only to return service object. If the chains of calls are long, testing becomes problematic.

Page 11: Magento code testability: Problems and Solutions

Law of Demeter Violations » Solution

Bad: Create mocks for context objects that will return themselves on every call except predefined stubbed calls.

Good: Refactor code to eliminate context objects. Depend only on objects that are required for delivering business goals of objects under test.

Page 12: Magento code testability: Problems and Solutions

Global State And Behavior

Global mutable state is a reason for most bugs. It is unreliable for code that uses it.

Global state decreases code testability. Code that uses global state can not be tested in isolation. Developer must reproduce global environment of a unit to test it.

Global behavior is a killer of testability. It can not be mocked or stubbed for testing.

Page 13: Magento code testability: Problems and Solutions

Global State And Behavior in Magento

• Global arrays• Global variables• Global factories• Mix (state + behavior)

Page 14: Magento code testability: Problems and Solutions

Global State » Arrays

Mage::registry, Mage::register and Mage::unregister form an interface of global dynamic service locator simply wrapping mutable array with global behavior that restricts access to array but makes it harder to emulate in testing environment

Page 15: Magento code testability: Problems and Solutions

Global State » Objects And Variables

• Mage::app()• Mage::getConfig()• Mage::get/setIsdeveloperMode()• Mage::getIsDownloader()

Page 16: Magento code testability: Problems and Solutions

Global State » Factories

Global factories localize important part of application behavior – object creation. They eliminate direct dependencies in code. Which is good.

But instead of implicitly depending on created objects, code that uses global factories starts to implicitly depend on them.

Also global factories cannot be substituted in test environments.

Page 17: Magento code testability: Problems and Solutions

Global State » Factories » Examples

• Mage::getModel()• Mage::getResourceModel()• Mage::getControllerInstance()

Page 18: Magento code testability: Problems and Solutions

Global State » Mix (Behavior + State)

These are dangerous in code and are hard to substitute in tests:

• Mage::getSingleton()• Mage::helper()• Mage::getResourceHelper()

Page 19: Magento code testability: Problems and Solutions

Global Behavior and State » Big deal

The big problem with global state and behavior in Magento is it’s used everywhere. All the dependencies of objects are pulled from global state instead of being pushed (injected) into these objects.

We cannot simply refactor our code to eliminate global dependencies. We would have to rewrite magento.

Page 20: Magento code testability: Problems and Solutions

Global Behavior and State » Solution 1

Build “Magento Unit Testing Framework” on top of PHPUnit, write testing-environment-special Mage, make it “mockable” and test our code “in isolation”.

This is an absolutely viable solution that will let us unit test our code in isolation avoiding massive refactoring.

Page 21: Magento code testability: Problems and Solutions

The problem

The problem with this solution is the same as with bad solutions described in previous sections: It fights the symptom (code non-testability), not the disease (global state).

And mutable global state and behavior is the reason of hard to debug type of bugs and encourages bad practices.

Page 22: Magento code testability: Problems and Solutions

The problem » Bad practices

• Liar interfaces• Law of Demeter violations• Deep code dependencies• Liskov substitution principle violations• Separation of concerns violations• Data envy• ….

Page 23: Magento code testability: Problems and Solutions

Global State » Solution

• Declare all object dependencies explicitly as entries in constructor argument array, and all the method-specific arguments as method’s parameters instead of pulling them from global state. Use object managers instead of global arrays when needed.

It will make our interfaces honest. And most code smells will become visible by only looking at method signatures. LSP violations will be noted by interpreter.

Page 24: Magento code testability: Problems and Solutions

Global Behavior » Solution

• If an object must create other objects then it must declare dependency on object factory, that will be injected into the object.

It will instantly show objects that create too much.

Page 25: Magento code testability: Problems and Solutions

Global Behavior » Solution

• If an object must create other objects then it must declare dependency on object factory, that will be injected into the object.

It will instantly show objects that create too much.

Page 26: Magento code testability: Problems and Solutions

Global usage of Global » Solution

• All the constructors will check presence of dependencies in arguments, if an argument is not present – it is taken from Global.

It will let us avoid massive refactorings. Because developer will only have to refactor the object he tests – not all the code that uses it. This can be a stage of transforming magento code to prepare it for DiC.

Page 27: Magento code testability: Problems and Solutions

Sources

• http://misko.hevery.com/• http://martinfowler.com/articles/injection.html• Test-Driven Development by Example. Kent

Beck

Page 28: Magento code testability: Problems and Solutions

Anton KrilKyiv Dev Folks [email protected]

Author