What strategy would you recommend to unit test an existing Grails app?
I've just read Beck Kent's book on TDD and would like to apply similar approach to my app.
My aim is to unit test the entire code base and to be able to refactor the code and make it "cleaner". By "cleaner" i mean I want to reduce duplication, make my controllers slimmer by extracting common logic into services and so on.
So where should I start? Models? Controllers?
What is your "bad" and "good" experience doing similar thing?
@Péter.
My app is not too big in my opinion. it consists of 12+ models, similar amount of controllers, few services and around 15 utils classes.
One of the main reasons I want to have full unit test coverage is that in many cases the system just works. While it's good from the user point of view from developer point of view such code is a nightmare to change and maintain.
Another important thing I would like to make small and fast regular releases (new little features and/or improvements), but without unit test coverage it would be almost imposible.
So the question is not: "Do i need to do it?", but "How can I do it?"
Depends on the size of the app, but for any nontrivial real life app it is a huge effort to satisfiably cover it with unit tests. So you need to prioritize your efforts, to focus on the most critical / most frequently changed / most buggy parts of the system (usually these overlap quite a bit: the most critical parts are usually the ones most often touched to add new functionality or to fix bugs).
A good method is to write unit tests more or less in TDD fashion whenever you need to touch any part of the code. I wrote "more or less" because for legacy code, you typically need to write much higher level, more complex unit tests than for greenfield development. In fact, in some cases it may not even be productive to start with unit tests, instead it is better to create functional/system tests to cover large grained functionality from the user perspective.
Note that, depending on the available documentation and level of developer/user knowledge about the system, you may not always be able to determine the "correct" behaviour for a specific functionality, only its current behaviour. Even in this case, it is worth covering it with (unit) tests: these document the code's actual behaviour and detect any unexpected changes in it in the future.
Once you have the actual piece of code reasonably covered with unit tests, this gives you the confidence needed for refactoring. Do some (simple or more complex) refactoring whenever you touch the code. However, don't overdo it. If you need to change a single line for a bugfix, it may be overkill to start refactoring a whole inheritance hierarchy (even if it is really messy). Make notes about such impending refactoring tasks and try to schedule them later.
I generally agree with @Peter in that this is a non-trivial thing to do, and you might be hampered by just knowing what things do, not what they are supposed to do. But thats ok, if not ideal, because testing is just as much about maintainability (i.e. knowing when changes break things) as it is about correctness. So put tests around the existing functionality as it is right now, and if you have to fix a bug, you can change your test to capture the fix.
If you and your team is focused on adding new features, then implement TDD now, i.e. test the new functionality, and add in tests for the existing functionality you are modifying, as was already suggested.
If you can focus on testing, then I would test in vertical layers. Write some tests for a model, then its services, then the controllers (that order is just a suggestion). The point is if you are testing the 'Book' part of your app, test all Book functionality before moving on. Also, you can prioritize on the core functionality of the app. If 'Books' are much more important than something else, test 'Books' first.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With