Hitchheikers guide to... part 1 – testing and tools what is testing and why to test ? what we test and how ? what tools we can use ? part 2 – TDD TDD as a development process why to bother (benefits and drawbacks)
Part 1 – Testing and tools
What is testing ? Validation and verification of the current implementation AND design Emulation of application state in the very specific moments It is a skill, like any other
What is testing ? – a little more… Measurable contribution to “implementation” value Testing does not equals writing tests Yes, it is looking for bugs…
Why to test ? Makes us better professionals Gives us – developers - valuable feedback Brings quality assurance to our work Enables us with confidence
Why we do not write tests ? (excuses) It is slow and time consuming Does not catch real bugs I do not write buggy code – sure… Debugging is faster
Why we do not write tests ? (excuses) We have untestable design Need to be refactored I am programming GUI I just do not know how…
Kinds of bugs Logical, aka. classic bug – errors in logical conditions, loops, etc. Wiring bugs – wrong data bindings, incorrect injections Rendering bugs – application output is messed, either UI or reports (unfortunately “tested” only by human)
Kinds of bugs Depending on kind of bag, difficulty of finding and fixing it varies…
Cost of fixing Logical bugs are hard to find and fix. Also, they are very common… Wiring bugs are relatively easy to find and fix, as their behavior is more verbose than logical one… Rendering bugs are usually easy to find and fix. It is really easy to SEE them…
Kinds of tests Depending on theirs kind, System wide teststests have different purpose. For true quality assurance, all tests are needed. The Integration testssmaller the test is – the more precise answer it brings. Unit testsThe catch is – the smaller the tests the more of them we need !
Unit tests Focus only on logical errors Are related only to a single class or method (isolation) Are very fast (only few ms)
Unit tests Are short and brief, when fail you do not need debugger to find the bug Are aimed to help when refactoring When passed – you are sure correct logical flow in class or method They are preferred way of testing !
Integration tests Test parts of application working together Are aimed to catch bugs related to objects relations Brief enough to be run before submitting to version control
Integration tests Can test boundary conditions unavailable in system wide tests Verify correctness of system integration with external systems practice physician patient
System wide tests Test correctness of the system as a whole Emulate user behavior (real use cases coming from analysis) Aimed to prove that system is capable to fulfill user (customer) requirement Can run noticeably long
What makes testing hard ?Four main factors of hard-to-test code: The way object is created (constructor logic), How classes interact with each other, Global state, Violations of OOD rules like LoD or SRP, LoD – Law of Demeter SRP – Single Responsibility Principle
LoD ? SRP ? – hmm….Law of Demeter – one of rules of good object oriented design which says: “Only talk to your best friends” ! bad: Car.getEngine().getFuelSystem().getTank().checkFuelLevel(); good: Car.getFuelLevel(); which calls Engine.getFuelLevel(); etc.
LoD ? SRP ? – hmm….Single Responsibility Principle – usually referred as: class should have ONE and ONLY ONE reason to change.
Why object construction makeslife harder ? Intensive logic in constructor denies possibility of unit testing and prevents testing If construction of the class depends entirely on implementation not interface, testing is impossible Complicated and time consuming constructions involves equally complicated test setup
Does it really matter ?Look howdependency onclassimplementationmatters…We can not testwithout theEngine !
How to prevent ? Do not bind object directly to an implementation, use interfaces where possible, Encapsulate object creation into factories, especially complex one
How to prevent ? Avoid intensive logic in constructors, Avoid “fake API” when creating objects (like .setup(), .init() method). Created object should be ready to use.
Class relations Main types of relations are: Instanced objects – new Passed by objects – as method parameters Dependency to state (global) objects
Untestables - class relations Dependence on specific instance denies testing (or at least makes it very painful…), Cyclic dependencies will make code untestable, Local dependencies (method) are hard to test,
How to prevent ? Favor interfaces over implementations, Keep away from instanceOf, Dependency injection makes life easier,
Untestables – global state Can cause most weird and hard to test situations, Test isolation principle is violated ! (order of tests matters…) Multiple executions of tests can cause different results,
Global state – common flaws Global state in JVM: System.currentTime(); Math.radom(); Static methods – you cannot change behavior using polymorphism, Singletons – root of all evil considering global state,
How to prevent ? Do not use static – it can be usually easily distilled into particular class or its behavior, Just avoid global state, If have to use global state - remember to “reset” it at the end of test method
Untestables – OOD violations SRP – the more incoherent class is the harder is to write tests. Highly incoherent class are hard to be unit tested. LoD – the deeper we go into object hierarchy the more difficult test setup is. Also, later refactoring becomes hard and painful.
How to prevent ? Inability and difficulty of wiring unit test are signs of bad architecture and/or bad design. Considering SRP, class responsibilities can be always distributed among more objects.
General flaws Complicated conditionals (wide ifs or switch), Depending heavily on Context objects, especially global flags, Static initialization blocks,
General flaws – a little more Hard dependency on external systems or modules, impossible to mock, Overuse of “utility” classes, * Final classes and methods
Tools for testing TestNG – successor to JUnit, testing framework aimed to be powerful and easy to use, Mockito – quite new mocking framework, simpler and easier in use than EasyMock,
Part 2 – Test Driven Development
What is TDD ?Test driven development brings alternative approach to classical software development cycle.
What is TDD ?TDD manifests itself in: Iterational (evolutionary) approach to development, Process emphasizing design over implementation, Continuous integration and refactoring,
TDD and its Test First DesignThe TDD bases on three key steps: Test Implementation Refactoring
TDD in snapshot You write business logic only when test is falling If all is running – clean implementation Developer focuses on requirement before writing actual implementation Resulting implementation is more cohesive and lowly coupled code
TDD tools that help Continuous Integration (Hudson, CruiseControl) Test coverage tools, like Cobertura (EclEMMA), xUnit / mocking framework (Mocquito, EasyMock), Static analysis tools like Classcycle (JDepend),
TDD tests first - benefits Starting from tests gives you long-term benefits: Helps keep your code clean Brings feedback to design and architecture early, Gives you constant and reliable feedback about development progress
TDD benefits – a few more Overall implementation time is shortened Tests are more through than in traditional approach
TDD tests first - drawbacks However there are some drawbacks: TDD demands “different” approach to development, Tests bring maintenance overhead, TDD is as successful as written tests
TDD drawbacks – few more… Process itself does not excludes wrong implementation (misinterpreted requirements), With time, bunch of passing always green tests can lead to false belief about implementation
Key factors of testingThe main drawback of testing and TDD is its dependence on two specific factors: people and problem to be resolved. Where people are we, the one who are supposed to create application for customer and second factor is… The customer itself, time, resources andcomplexity of the problem…
TDD myths 100% test coverage is required, Unit tests are enough, http://de.wikipedia.org/wiki/Datei:Wolpertinger-praeparat.jpg Development effort is greater, and takes longer, TDD has long learning curve *
A word for end If its worth building, its worth testing.If its not worth testing, why are you wasting your time working on it?