Monday, May 31, 2010

Are tests a burden?

Sometimes one might even think so (actually today for some moments I did). The story: one of our factories "left out" a property, meaning that we simply forgot it, both in tests and codebase. Do I hear someone say "then what are tests for? they are useless as they didn't trap your error"? well the someone I'm thinking about is on holiday so I'll be spared that, but she would be right on one thing: the test, these unit tests at least, did not trap MY error (assuming I wrote that part of the code).

So what? this oversight, when corrected (test first of course), caused some tests related to distant classes to fail, not because the correction was not right, but because the freshly failing tests needed to be updated.

Foolishly, I didn't run the complete suite of tests for the project before committing, so my CI server wrote me that I broke the build. This happened twice.

I must confess it was anoying: I had to add more code to correct the wrong assumptions of the test. This conceptually disturbed me, as I didn't have to fix the code (which was already correct) but the tests (it just so happened it was because I was using a mocking library and I had to set another couple of expectations).

This pushed me to a couple of considerations.

First, it is true that sometimes mocking libraries introduce a dependency you could happily live without.

Second, I would have avoided to trigger a remote build if I ran the whole suite of tests. The problem is that it would have take too long compared (in my opinion) to the small changes I made. Once more, I slammed my face on the wall, so I think I'll play ball by always applying the "Run Private Builds" pattern, that in the end would have saved me time.

Thursday, May 27, 2010

Speak of Talking

And he answered, saying:

in much of your talking, thinking is half murdered.
For thought is a bird of space, that in a cage of words may indeed unfold its wings but cannot fly

Gibran Kahlil Gibran


I have found this concept in many different places, but seldom so clearly and with such beautiful words.

Wednesday, May 26, 2010

But what of our Laws, master?

And he answered:

People of Orphalese, you can muffle the drum, and you can loosen the strings of the lyre, but who shall command the skylark not to sing?

Gibran Kahlil Gibran

How to get a byte array from a String

Just a snippet which willingly overlooks the fine prints about "strange" characters:

String myString = "Oh what a wonderful string!"
byte[] bytes = myString.getBytes();

Speak to us of Children

And he said:

You may give them your love but not your thoughts,
For they have their own thoughts.
You may house their bodies but not their souls,
For their souls dwell in the house of to-morrow, which you cannot visit, not even in your dreams.
You may strive to be like them, but seek not to make them like you.
For life goes not backward not tarries with yesterday.

Gibran Kahlil Gibran

Few times I have read a book of such a rare beauty. Read it aloud, and your awe will grow and grow. Absoluterly a must-read.

Thursday, May 20, 2010

NetBeans and JAX-WS

A failing hard disk can be a positive thing: at least it forces you to learn new things. After fixing the project I just wrote about, I went straight to the next one, checked it out, fixed all the paths on the database configuration files (this time I was on the ball), cleaned and built, tested it and everything was fine. A big smile was spreading on my face, as now I could really go back to the real stuff.

Until I ran it.

20-mag-2010 17.53.23 com.sun.xml.ws.transport.http.servlet.WSServletContextListener contextInitialized
INFO: WSSERVLET12: JAX-WS context listener initializing
20-mag-2010 17.53.27 com.sun.xml.ws.transport.http.servlet.WSServletContextListener contextInitialized
GRAVE: WSSERVLET11: failed to parse runtime descriptor: javax.xml.ws.WebServiceException: Unable to create JAXBContext
javax.xml.ws.WebServiceException: Unable to create JAXBContext

(blah blah blah jabber jabber jabber blah blah blah)

Caused by: java.security.PrivilegedActionException: com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 2 counts of IllegalAnnotationExceptions
my.wonderful.package.MyWonderfulClass is an interface, and JAXB can't handle interfaces.

(ariblah blah blah jabber jabber jabber blah blah blah)

Actually the blah blah part was much longer, but it left me quite disappointed. Everything had always worked fine, and we hadn't touched that codebase for quite a long time. It must have been something about configuration, but NetBeans didn't complain about missing references.

I googled for a while to get some clues, and everything I found required an intricate scaffolding over our classes. Too bad.

The only remaining culprit could only be NetBeans, as Marco pointed out - no, I won't pass to Eclipse, though. And that's final. Neither to Maven, at least for the time being :-)

Comparing a 6.7.1 installation with a 6.8 I found out that the first reported the JAX-WS 2.1 library in the dependencies of the project, while the latter the 2.2 version. At first I missed it, as the first place I checked was obviously the project.properties, that only reported a reference to the ${libs.jaxws21.classpath} variable, which was the version we were actually using. As setting up another project NetBeans kindly informed me that I was using JAX-WS 2.2 but the features I required needed the 2.1 version and I had to use the endorsing mechanism, I was quite ready for something like that, but it all passed by silently and I (wrongly) felt reassured.

I really had to double check... as I realized comparing the build.properties file used by the two IDEs, that referred to the same variable but had different path elements. What a fool! The more I find out, the less that I know...

Create or update?

A couple of days ago a bunch of sectors of my hard disk decided to vanish into the blue, bringing with them some files of my local repository (no worry about that) and some files of my NetBeans installation, configuration files included (no worry about that too, but a little more work required). While my colleagues are providing me with a new hard disk, on which I asked for a fresh installation (before you ask: it is a company policy that we only use operating systems from Redmond on our personal computers) I am working on the laptop. Which, by chance, happens to be a fresh installation too as it is the replacement for my old one (who can nowadays use NetBeans AND Firefox AND SQLServer AND all the rest, antivirus included, on 1GB? Maybe 2GB is not that much, but at least it doubles my previous supply)

Ok, so after reinstalling and reconfiguring my stuff I checked out a group of three projects we're working on. Projects that our Hudson guarantees as healthy (see one below).


Open the group of projects, set as main, fix the configurations for my local databases (for each database we have one instance for manual and acceptance tests and one for the automated ones) configurations clean and build, test (just to be sure).

91% passed. What? That means fail! but... what's wrong? mmm... it seems there's a row in the User table that causes Hibernate to complain about constraint violations. That is strange, as - as far as I know, but I obviously might be wrong - all our automated persistence tests run with HibernateOpenViewSession, a simple Runner that before each test opens a Session and rollbacks everything after the invocation (nothing new under the sun, but quite handy):

@Override
protected void invokeTestMethod(Method method, RunNotifier notifier) {
try {
final Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();

super.invokeTestMethod(method, notifier);

if (session.isOpen()) {
session.clear();
session.getTransaction().rollback();
}

if (session.isOpen()) {
session.close();
}
} catch (Throwable t) {
System.err.println("HibernateOpenViewSession RUNNER FAILURE");
notifier.fireTestFailure(new Failure(Description.EMPTY, t));
}
}

Yet, that row is still there. Well, maybe one of our tests doesn't use our favourite runner. So, why doesn't Hudson complain? After a little investigation it turned out that the Hibernate configuration for our CI server has a hibernate.hbm2ddl.auto=create property, where my local file had update. That fixed, all my local files ran smoothly and gave me a green bar.

Now the hunt for Red October will start. Ehm... I meant the hunt for the culprit test, of course... I'll keep my eyes on the ball.

By the way, we almost reached our 1000th test on the project :-) and it is only part of a group of three... it may sound a little to many out there, but for us it is a great success.

As a parting note, let me stress again the importance of a CI server to avoid the "it runs on my machine" syndrome - which in this case was "it doesn't work on my machine" :-)

Friday, May 14, 2010

What about my failing test?

After all, it turned out that my modified interceptor was not behaving correctly. Woah, wait a minute... didn't we have a test for that? Yes we did.

Yet, the user test failed.

What did I write the tests for? was it only an addictional burden, like someone suggests here? On the contrary. Well, when you write a test you actually are stating that "given X, when Y then Z". Unit tests are about "when Y, then Z", as when you write them you are supposed to know what X is (otherwise it would not be given). As I positively knew that the "when Y, then Z" part was working, the diagnosis was easy: my assumptions were wrong. A couple of clickety-clackety-clack and I collected the necessary data to build up the fixture. Ta-daaa! (yes, another red-green-refactor cycle followed).

TDD is a time saver, not a burden. And you don't need to be a master of design, like Brett suggests, as TDD forces you to concentrate on desing, even at an unconscious level. Whenever you think "how the hell am I going to test that?" the code is suggesting that you're taking a wrong way. Your tests are the first clients of your classes. Treat them well, or else they will stop buying. And finding a new customer is much more expensive than keeping the ones you've got, which means that you won't write tests. It's a descending spiral: keep out of it.

Thursday, May 13, 2010

Write a failing test first

Checking a functionality of a Struts2 interceptor I realized that we forgot to check a condition. Adding the correction was trivial, but I resisted. I first added a failing test, even though the class had no tests at all (this was justified by the fact that most methods are actually pretty simple one-liners).

Yet, an error lurched its way from behind.

The answer is NOT the code fix. At least, not only the code fix. Because other errors are lurking, and I don't want to be in the balance: I want to avoid regression(s). There are no excuses, as testing with Struts2 is much easier than it was with Struts.

That said, I wrote two test cases to avoid regressions on what I knew was working. Green. Then I added another test case that checked the failing condition. Red, as supposed. Only at this point I could add the magical line of code I needed. Back to green. Now we're going somewhere. And I left my camp (slightly) better than how I found it.

Then it was time for some riskless refactoring (well maybe not riskless, but close). Following Steve Freeman suggestions, I changed the names of a couple of methods to make them more expressive. Of course I could have done this before, but having a test harness makes you feel less... vulnerable. Once you get the habit, it is hard to get rid of it. And I don't want to get rid of it :-) on the contrary, I want to keep the ball rolling.

System administrator?

System administrators have a very important role in ICT. It is their responsibility to guarantee a whole bunch of things, from security to availability, from auditing to you name what. They must be respected for their work, which is a hard one indeed.

System administrators also have another responsibility: allow users to do their work. Despite all maintenance work a network might need, when someone asks "where has that network share gone?" I think an answer is due, least it be "mind your own business bwhah hah hah hah!!!". But maybe it's too difficult a question.

Well, I saw that happen. And when finally someone found out (...) the answer was "reboot and you'll see it". Wow, logon scripts at their full power. But wait a minute, why the reboot? conldn't we simply use a network address? I don't think it's classified, even because the simplest net use would tear the veil of secrecy. Anyway... guess what happens after the reboot? Yep. Nothing. Mysteries of ICT. Or are they? Some things really throw me off balance.

Being a sysadmin is not a privilege: they're here to serve everyone else (yes, also the CLs, I'm sure that deep - maybe very deep - in his heart also the great Davide knows it). I know, because I've done it. I only hope I wasn't like the ones above. But maybe some of our politicians are too much of an inspiration.

From great power comes great responsibility.

Thanks Uncle Ben. I wish more people understood that.

Wednesday, May 12, 2010

Can't rain all the time...

Even if it's a quote from The Crow, at the moment I feel more like a Kaminoan...


I guess it won't take long before our necks start to lenghten... at least I will look thinner!

After all this rain has some positive side effects, e.g. my wife has not killed me yet despite the fact that I still haven't recharged the batteries of the watering system...