Maintain your tests
First of all - I haven't been writing here for a while. Various things kept me busy, and I didn't think I had anything new to talk about. Probably this topic isn't very new either, but recent events made me elaborate on it.
Refactoring is probably one of the most practiced activities nowadays - specially if you're aiming to be good at writing software. More domain knowledge and experience will make that beautiful piece of code you wrote a couple of months ago look terribly ugly now. Modern IDEs and a good safety net provided by our tests make this task easy and enjoyable.
However, and specially when you're working (like I am at the moment) on a very big project, maintaining your tests is actually as much important as maintaining your production codebase.
Code sometimes may not be crystal clear at first sight (and in some cases not even after reading it a couple of times), so what really helps us understanding code is tests. Tests should express requirements - exactly what the code is supposed to do. But happens quite often that, even with a proper test coverage, tests are the dusty corner of our codebase: people tend to write them, but seldom refactor them. This makes really difficult both understanding the test itself but also to extend it, if there's a need to.
Few problems I usually come along, and how I try to solve them:
- Poorly named tests: lots of people start their test names with
test...
in reminiscence of the old JUnit 3.x days. A test name describes one of the requirement our tested part of the system needs to implement. A test name should make sense as read in plain English, and it should describe one of the capabilities of the system (i.e.acceptsRequestMoreRecentThanThreeDays
). - Tens of assertions, sometimes unrelated to what the test is testing: people usually say that you should have only one assert per test. I'm not that strict, but assertions that are in the same test should be related (yeah, that's the same high cohesion thing!). If a test is checking whether some additional work is done in case of a particular input, as long as you've covered the other work your method will always do in another test there's no need to repeat the assertions. It will mislead the reader from the real purpose of your test. Remove them: it will make the test clearer and shorter.
- Mocks, mocks everywhere: again a controversial topic. I read many people that tell that mocks are useless - I think that they are a powerful tool when doing unit testing. Some units of work won't change state of objects, while they will produce side effects (e.g. call a DAO). By mocking dependencies (I should rather say by creating stubs, but some tools like Mockito made the name mock more universal) and verifying invocations, we test that our code is really doing what we want. But lots of times I've seen mocking value objects owned by the application, and even setting answers to getters. Interactions on value objects should be always tested by checking the state of the objects: it's easier and less error prone (you remember that guy that added some hidden logic in a getter, don't you...). As a rule of thumb, if a dependency is mocked to return something, that something probably shouldn't be a mock itself.
I like my objects immutable
Having practiced a lot of functional programming recently (I've just finished the Functional Programming Principles in Scala online course on Coursera, and I loved it!), I came (again) across the topic of immutability
For those who don't know the word, an immutable object is an object whose state can't change after it has been built (i.e. you cannot add elements to a collection). In some languages such as Scala, immutability is achieved without too much hassle (actually Scala collections are immutable by default, unless you specifically choose to import their mutable version), for other languages such as Java there are few tricks to make objects immutable (using constructor / builder objects for setting up state, using Immutable Collections to build your collections, have no setters / all final fields, marking its class as final and so on).
Even if constructing an object to be immutable is not immediate, I always try to make the objects I wrote immutable, and I think you should as well! There are few reasons for this:
- An immutable object is inherently thread-safe. Since its state cannot be changed, there are no race conditions when trying to access the object concurrently, hence it can be shared freely without the need for any synchronisation on it.
- An immutable object is constant. This leads to various advantages, such as it can be used as a key to a map. If you're using a map that calculates the hash of its key, an immutable object will always have the same hash. It can be cached, and it allows you to truly rely on the hash to retrieve an object (what would happen if you use a mutable object as a key then you change your state?)
- It's easier to reason with an immutable object, as you know that once it's been created its state will be always the same. You can avoid defensive code practices (copy constructors, copy of fields) when you deal with immutable objects.
- It forces you to write methods which don't have side effects: if you pass an immutable object as a parameter to a method, you are certain that the object won't be in a different after the method has been executed. This makes debugging issues much easier!
As I said before, there are libraries such as Guava that makes this task easier. Unfortunately, in some situations making an object immutable is not possible, but in that case we should aim to reduce mutability as much as we can.
Spock: mind your asserts!
Recently I've been involved on some work on a Grails app, for which specifications are implemented using spock framework. It's a very readable and solid spec framework, which I also highly recommend. One way of writing spock specification is using some kind of BDD-style constraints, like:
[gist id="ae961652b7a0b0b50e1a"]
As you can see, expression written in the then:
block are treated as asserts, and your test will fail if one of the conditions is false.
Sometimes, we need to assert more conditions, and for better readability of our tests we use some private methods, like:
[gist id="01635c97762a4eaa049b"]
The subtle thing here is that, since we're not in the spock dsl anymore, conditions in private methods are not treated automatically as asserts, and we need to add the assert
keyword in order to have our test working properly.
After discovering that we had to fix a bunch of tests that were written incorrectly...
This also leads to another key concept - always make sure that your test fails for the reason you're expecting it to fail. While doing TDD this comes at the very beginning of the process, if you're not applying TDD you have to change your code accordingly to make your test fail.
Do you believe in Software Architects?
When I started to work professionally as a developer, I was fascinated by the role of software architect. Somebody who could design software, decide how modules interact together, write lots of diagrams and specify a system.
I think those days are long gone.
Although my career hasn't been particularly long so far, I've already seen lots of changes in the software development world. Maybe because I started in an environment which - for questionable reasons - wasn't really up to date with emerging trends (even though XP wasn't really emerging anymore in 2008!), but I feel it's been ages. It's been ages since we felt we needed UML class diagrams, it's been ages since we thought inheritance was the solutions to all (ok, not all, but lots of) duplication problems, it's been ages since we thought that code was the problem.
People working as developers would progress their careers - and both they decided to go into management, or stay "techincal" by becoming architects, they'd spend less and less time coding. As all aspiring software craftsmen know, coding is a craft, and a craft can only be perfected with practice. Think about how do you feel going back to code, if you had a two week break without a keyboard. Surely you'll feel comfortable again not in a long time, but you'll always have a feeling of rustiness.
Often, software architects spend their days in meetings with management, thinking about what product to buy (based on the one they feel comfortable with...) and to use in the next big upcoming project. And, unfortunately often, development teams have to live with the decisions made by architects, that in a very good number of cases don't have a proper idea of the real state of the application, and why a particular decision could or could be not appropriate to the project.
Development teams should be empowered to have decisional power on technologies and software architecture. After all, only the one who puts his hand in the code on a daily basis is the one that knows which tool is fit for the job, or if you should separate layers in your applications. Of course probably not everybody in the team would have the experience to make an informed choice, but a well formed team would definitely make a better choice. After all, they'd pay the price of a wrong choice at the very beginning. Teams that choose their tools and systems are also the teams that feel their project really "their project", improving productivity and quality of the software.
Do you really think you need a Software Architect? Or you just need to have experienced, trusted and adequately empowered developers?