Testing the Impossible: Shifting IDs

7. October, 2009

So you couldn’t resist and wrote tests against the database. With Hibernate, no less. And maybe some Spring. And to make things more simple, toString() contains the IDs of the objects in the database. Now, you want to check the results of queries and other operations using my advice how to verify several values at once.

During your first run, your DB returns some values, you copy them into a String. When you run the tests again, the tests fail because all the IDs have changed. Bummer. Now what? Just verify that the correct number of results was returned?

Don’t. assertEquals (3, list.size()) doesn’t give you a clue where to search for the error when the assert fails.

The solution is to give each ID a name. The name always stays the same and when you look at the test results, you’ll immediately know that the ID has been “fixated” (unlike when you’d replace the number with another, for example). Here is the code:

import java.util.*;

/** Helper class to fixate changing object IDs */
public class IDFixer
{
    private Map<String, String> replacements = new HashMap<String, String> ();
    private Map<String, Exception> usedNames = new HashMap<String, Exception> ();
    
    /** Assign a name to the number which follows the string <code>id=</code> in the object's
     * <code>toString()</code> method. */
    public void register (Object o, String name)
    {
        if (o == null)
            return;
        
        String s = o.toString ();
        int pos = s.indexOf ("id=");
        if (pos != -1)
            pos = s.indexOf (',', pos);
        if (pos == -1)
            throw new RuntimeException ("Can't find 'id=' in " + s);
        String search = s.substring (0, pos + 1);
        
        pos = search.lastIndexOf ('=');
        String replace = search.substring (0, pos + 1) + name + ",";
        
        add (name, search, replace);
        
        registerSpecial (o, name);
    }

    /** Add a search&replace pattern. */
    protected void add (String name, String search, String replace)
    {
        if (usedNames.containsKey (replace))
            throw new RuntimeException ("Name "+name+" is already in use", usedNames.get (replace));
        
        //System.out.println ("+++ ["+search+"] -> ["+replace+"]");
        usedNames.put (replace, new Exception ("Name was registered here"));
        replacements.put (search, replace);
    }
    
    /** Allow for special mappings */
    protected void registerSpecial (Object o, String name)
    {
        // NOP
    }

    /** Turn a <code>Collection</code> into a <code>String</code>, replacing all IDs with names. */
    public String toString (Collection c)
    {
        StringBuilder buffer = new StringBuilder (10240);
        String delim = "";
        for (Object o: c)
        {
            buffer.append (delim);
            delim = "\n";
            buffer.append (o);
        }
        return toString (buffer);
    }
    
    /** Turn a <code>Map</code> into a <code>String</code>, replacing all IDs with names. */
    public String toString (Map m)
    {
        StringBuilder buffer = new StringBuilder (10240);
        String delim = "";
        for (Iterator iter=m.entrySet ().iterator (); iter.hasNext (); )
        {
            Map.Entry e = (Map.Entry)iter.next ();
            
            buffer.append (delim);
            delim = "\n";
            buffer.append (e.getKey ());
            buffer.append ('=');
            buffer.append (e.getValue ());
        }
        return toString (buffer);
    }
    
    /** Turn an <code>Object</code> to a <code>String</code>, replacing all IDs with names.
     * 
     *  <p>If the object is a <code>Collection</code> or a <code>Map</code>, the special collection handling methods will be called. */
    public String toString (Object o)
    {
        if (o instanceof Collection)
        {
            return toString ((Collection)o);
        }
        else if (o instanceof Map)
        {
            return toString ((Map)o);
        }
        
        if (o == null)
            return "null";

        String s = o.toString ();
        for (Map.Entry<String, String> entry: replacements.entrySet ())
        {
            s = s.replace (entry.getKey (), entry.getValue ());
        }
        
        return s;
    }

    public boolean knowsAbout (String key)
    {
        return replacements.containsKey (key);
    }
}

To use the IDFixer, call register(object, name) to assign a name to whatever follows the number after id=. The method will search for this substring in the result of calling object.toString().

If you call IDFixer.toString(object) for a single object or a collection, it will replace id=number with id=name everywhere. If you have references between objects, you can register additional pattern to be replaced by overriding registerSpecial().

To make it easier to compare lists and maps in assertEquals(), each item gets its own line.


Testing The Impossible: Inserting Into Database

5. June, 2009

Tests run slow when you need a database. An in-memory database like HSQLDB or Derby helps but at a cost: Your real database will accept some SQL which your test database won’t. So the question is: How can you write a performant test which uses the SQL of the real database?

My solution is to wrap the JDBC layer. Either use a mock JDBC interface like the one provided by mockrunner. Or write your own. With Java 5 and varargs, this is simple:

public int update (Connection conn, String sql, Object... params) throws SQLException {
    PreparedStatement stmt = null;
    try {
        stmt = conn.prepareStatement (sql);
        int i = 1;
        for (Object p: params) {
            stmt.setObject(i++, p);
        }
        return stmt.executeUpdate ();
    }
    finally {
        stmt.close ();
    }
}

Put all these methods into an object that you can pass around. In your tests, override this object with a mockup that simply collects the SQL strings and parameter arrays. You can even mix and match: By examining the SQL string, you can decide whether you want to run a query against the database or handle it internally.

This way, you can collect any newly created objects but still load some background data from the database (until you get bored and make the query methods return predefined results).

In the asserts, just collect all the results into a big String and compare them all at once.

Notes: The code above is a bit more complicated if you allow null values. In this case, you need to tell JDBC what the column type is. My solution is a NullParameter class which contains the type. If the loop encounters this class, then it calls setNull() instead of setObject().


Testing the Impossible: Asserting Several Values At Once

23. March, 2009

So you want to check several values at once, maybe because that helps you to locate the error more easily. Simple:

int v1 = list1.size();
int v2 = list2.size();
int v3 = list3.size();

assertEquals (
      "list1=5\n"
    + "list2=2\n"
    + "list3=0\n"
    , "list1="+v1+"\n"
    + "list2="+v2+"\n"
    + "list3="+v3+"\n"

Verifying Results in Tests

5. March, 2009

So you finally got yourself writing a test against a database. In the test, you have to verify that a row in the table is correct, so you write:

assertEquals (
    "2008-09-16-13.50.18.000000;;;;1;2008-08-07;2008-08-07;JUNIT;2008-09-16;t0001;001;;Doe;Jane;;Street;2;Doe Jane;;;;;X;2575;John;;;;US;E;;01;;;;125;01425;0;Shop;;;DOE;JANE;JOHN;032;1;;0001010301;;;;",
    dumpRow(key));

and it sucks. Yeah, junit will notify you when something in that row is wrong and if you have a cool IDE, you can even compare the fields … but it still sucks. If one of the fields in the middle change, you have to scroll and eyeball-diff, wasting your time. The solution is pretty simple:

assertEquals (
    "2008-09-16-13.50.18.000000\n"
    + "\n"
    + "\n"
    + "\n"
    + "1\n"
    + "2008-08-07\n"
    + "2008-08-07\n"
    + "JUNIT\n"
    + "2008-09-16\n"
...
    dumpRow(key).replaceAll(";", "\n");

Instead of dumping the data in a single long string, split it into lines so you can compare fields side by side and without scrolling sideways.


Mutation Testing

23. February, 2009

From the website:

How do you know your test suite is “good enough”?

One of the best ways to tell is mutation testing. Mutation testing seeds artificial defects (mutations) into a program and checks whether your test suite finds them. If it does not, this means your test suite is not adequate yet.

Read more about Javalanche on the website.


On Blindly Following Advice

12. February, 2009

In his post “Thoughts on Developer Testing“,

“Unfortunately, you can’t write better software by blindly following dogma of ‘industry experts’.”

Just as “an eye for an eye” leaves everyone blind, staggering around in a changing environment with your eyes closed shut will not help you getting closer to any goal you might strive for.

A mind is like a parachute: It’s only working when it’s open.


Why Good Developers Don’t Document

10. February, 2009

I don’t document because I know what I’m doing. Seriously. I sit down in front of the computer and beautiful code flows out of my fingertips. No idea how I do it. It’s like digesting for me. It just happens. For me, with my vast experience, my code is so obvious that I can’t think of any question someone might have (okay, let’s just say I need a bit of distance to come up with questions, say a year or so). For me, it’s as simple to understand as hello world. So why make things worse by adding unnecessary comments?

I do understand that other people see this differently but that doesn’t help. I’m not those other people and I simply can’t think what questions they may have. In my case, the solution was unit tests. I need them anyway (they make me more productive) and for the rest of the team, they serve as example how to use the code I write. This is much better than documentation because

1. a test is small, so the answer to your question lies in 10 lines of code. 10 lines of code can’t take long to understand.
2. it is always correct and up to date (unlike documentation which tends to rot)
3. after getting used to test things, you feel how it improves your output (in all ways); documentation, OTOH, is always a drag (“we have to do it … not again … oh man!”).


Testing the Impossible: Hardware

5. February, 2009

Victor Lin asked on StackOverflow.com:Should I write unit test for everything?

In his case, he wanted to know how to test an application which processes audio: Reads the sound from a microphone, does something with the audio stream, plays the result on the speaker. How can you possibly test a class which reads audio from a microphone? How can you test playing sound on a speaker?

As Steve Rowe pointed out: Use a loopback cable. Play a well defined sound on the speaker and check what the microphone receives.

I suggest to move this test case into a separate test suite so you can run it individually. Print a message to plug in the loopback cable before the test and wait for the user to click “OK”. This is a unit test but not an automated one. It covers the setup steps of the hardware.

The next thing you will want to do is to check the code between the mic and speaker parts. These are now no longer dependent on the hardware and therefore simple to test.


Background Unit Testing

2. February, 2009

I just found this article: Background Unit Testing: New Evolutions in Unit Testing and IDE Integration
The idea is that your IDE should run the unit tests in the background just as it runs the compiler in the background. Compelling. There are already two implementations for Eclipse: JUnitMax from Kent Beck and Infinitest.


Testing: Pay in Advance or Afterwards?

2. February, 2009

In a recent post, I talked about people ignoring the cost of some decision. In his blog “Joel on Software”, they talk about the same thing: How easy it is to fall into the “we must have strict rules” trap to protect ourselves against some vague  fear of failure. Only, humans are really bad at sticking to rules. Or are they? Maybe it’s just that reality doesn’t care so much about rules because things change. If you built your castle on the belief how well strong walls will protect you, the swamp around the basement is not going to care. You’re going down, chummer.

So we end up with a lot of rules which make exactly one thing simple: To assign blame. I’ve been working for a big company where we have a strict process how projects were to be set up. There were lots of documents and forms and comittees how to start a project and a lot of documents describing how to end it (put it into production, what documents to file, who to inform, you name it). It was a great process (in the sense of “big”, mind). The actual writing of the code was explained in a document which contained a single page. On that single page, they talked on how they would strive to write excellent, error free code and that they would use a proven strategy, the waterfall model.

They built a huge, shiny castle on nothing.

If you go to a bank and tell them you have lots of $$$ and you need to pay some big bill somewhere in the future, their first question will be: How you want to make that money work for you in the meantime? Just letting it rot under your desk is not very smart, right? You should invest it somewhere, so you will have $$$$$ or even $$$$$$$ when it comes to pay the bill. Which makes sense. Contrary to that, when we write software, we tend to spend our money first instead of parking it in a safe place where it can return some revenue, being ever vigilant to be able to pay as the bills show up. Which is harder than just sitting back and relying on some mythical process someone else has written on a piece of paper a long time ago.

So when you ask: “Should I write tests for all my classes? For every line of code? How should I spend my money?” Then my answer will be: I don’t know. How can I? I know nothing about your project. But I can give you some ideas how to figure it out yourself.

“Should I write tests for all my classes?” That depends on what these classes are meant for. The more low-level the code, the more tests you should have. Rule of thumb: Tests yield more result in the basement. Make sure the ground you’re building on is sound. And behaves as you expect. The upper levels are mostly built from lego bricks. They are easy to take apart and reshape. They are exchangable, so you can get away with fewer tests. But every bug in the foundation will cripple anything above it.

“For every line of code?” No. Never. 1. It’s not possible. 2. Maintaining the tests will cost more than the real code. 3. Tests are more simple than the real code but you still make a constant amount of mistakes per lines of code. So this will only drive the number of bugs through the roof. 4. Strict, fixed rules never work (note the paradox).

“How should I spend my money?” One word: Wisely. Wisely means to think about your specific problem and find the unique solution. Do you know in advance how much each piece will cost? No. So the best you can do is a staggered approach: Invest a bit of money, check how it plays out. If it works well, spend more. If it doesn’t, scratch it, learn, try something else. Which you will be able to do since you didn’t put all your money on a single horse.

So what if your three month venture into agile development didn’t really work out? All you lost is three months. Other projects are deemed a “success” after going over budget by 100%, using twice the time that was estimated (and none of them were shorter than a year). But you will still have learned something. You paid for it, that wisdom is yours.

Use it wisely.