240 likes | 387 Views
Refactoring & Testability. Testing in OOP programming. No life in flexible methodologies and for refactoring-infected developers without SOME kind of AUTOMATED tests Tests are not a panacea, don't be categorical and skeptic. Tremendous use when possible to write
E N D
Testing in OOP programming • No life in flexible methodologies and for refactoring-infected developers without SOME kind of AUTOMATED tests • Tests are not a panacea, don't be categorical and skeptic. Tremendous use when possible to write • JUnit is a tool, so "не забивайте гвозди штангенциркулем" • Unit testing forces as to refactor!!!
Unit testing benefits (1 of 2) • Can be applied to incomplete application • JUnit is the first User of your code! • Kinds of tests Unit->Integration->Function->Stress • Characteristic is the amount of application build already • Makes me confident that I prevent my peer developers and QAs from finding tedious errors in my code. • Less code to debug under test (as opposed to functional tests)
Unit testing benefits (2 of 2) • Can simulate errors • Test not only successful path (as integration test basically does), but the scenario when something can fail! • Testing exceptions • Can act as a perfect and up-to-date documentation • Encourage us for refactoring! Probably the most important advantage.
Example. Testing exceptions: try { methodWhichThrowsException(); fail(“Description”); } catch(ApplicationException meaningfulNameExpected) { assertNotNull(“Explanation why expected”,expected) }
Mocking • Programming on interfaces vs. concrete classes • Subclassing from concrete classes is a testing smell • If I subclass and override a method, what do I actually test? • MockObjects.com (JMock) • No! business logic. The mock should 100% comply to contract under CURRENT test. JMock is well suited for this rule.
Example: • Testing e-mail service.
When to use mocks • Notes on page 4
Too many mocks? Hard to setup a test? • Mock lower OR refactor! • If a big framework has single but very smart method doWork() it's most likely a bad idea to test it • Test may be skipped if the method's contract is based solely on other contracts
Example: • Shopping cart test • (com.luxoft.tests.exercises.shoppingcart)
Refactoring • Hardness to write (set up) unit tests FORCES us to simplify (factorize) the method or class or module. • Bad code is impossible to well unit test! • there is one-to-one dependency between good code and ability to write good tests • Code smells (detect manually or by means of PMD, etc.) • Writing tests
Refactoring DON’Ts (1 of 3) • Make Your Own Dependencies • Heavy Duty Constructors • Depend on Concrete Classes • Conditional Slalom. if-branches and switch statements • Depend on Large Context Objects • Use Statics • Use Global Flags
Refactoring DON’Ts (2 of 3) • Use Singletons Everywhere • Use Primitives Wherever Possible • Look for Everything You Need (Law of Demeter) • Couple functional code directly to the external systems it depends on • Mix Object Lifecycles • Side Effects
Refactoring DON’Ts (3 of 3) • Create Utility Classes and Functions/Methods • Create Managers and Controllers • Do Complicated Creation Work in Objects • Utils! • Final Methods • Handcuff your users to Specific Types • Use static initializes
Example. Don’t depend on Large Context Objects: class Mechanic { Engine engine; Mechanic(Context context) { this.engine = context.getEngine(); } }
Example. Violate the law of Demeter: class Monitor { SparkPlug sparkPlug; Monitor(Context context) { this.sparkPlug = context. getCar().getEngine(). getPiston().getSparkPlug(); } }
Inversion of controls & objects construction • Hollywood principle vs. factory method principle • Collaboration graph and construction graph • Remove new operators from application code as much as possible • Conclusion: separate classes with business logic and factories which create classes
Example. Avoid new op. for testability: class House { private final Kitchen kitchen = new Kitchen(); private boolean isLocked; private boolean isLocked() { return isLocked; } private boolean lock() { kitchen.lock(); isLocked = true; } }
Benefits of IoC • Allows us to mock in tests • Allows us to use single instance of a class (saves us memory) • Shortens the methods and make them more precise (only business logic). • Real isolation.
Inversion of controls. Conclusion • It’s good both for tests and for code’s health to: • separate the business logic from factory code to create required objects (for that business logic) • Doing so is really boring! Let the computer do boring stuff. • Use IoC containers • Dependency injection (wiring) • Use empty, param-less constructors. Use setters!
Refactoring for testability. Final thoughts • Consider IoC vs. factory method • Example: Isolate system resources getRemoteDataObtainer() • Test coverage and test quality • 100% coverage does not mean that the method is 100% tested. Example. • Test quality measurement tools • Clover • Jester/Muclipse • Oil detector in a car’s engine
When Unit tests are good? (1 of 2) • Continuous and automated • Hudson / Email reports • Fast, easy to run • Written early to force Refactoring and detect design errors • No real opportunity to use integration tests • When code is good writing tests is fun. Should be easy
When tests are good? (2 of 2) • When there a lots of tests!!! • We MOCK a contract, i.e. believe that a mocked method follows the contract. Test that! • Test as less code outside of the method under test as possible. • Ideally: • test=(test_method_code) + (private_methods) + (mocks)
Unit tests is a must activity for a developer • Unit tests are not even a way to test code, but for a developer is a very much a way to develop perfect code • Just try!