How do I make them test?

9. March, 2010 at 13:43 | In Software | 1 Comment
Tags: ,

On The Daily WTF was a nice story: Unit Tested. It’s a story how someone tried developers to test their code and failed.

It’s always the same story and always the same reason: For a change in behavior, expect a year for it to become a habit. Under the best conditions.

So how do you introduce tests to developers? Here are some tips:

1. When they fix a bug, ask “Are you sure the fix is good? How do you know? Ah, you ran the code? Good. Then run the code once more but from a little test.”

2. When they discuss a new feature and can’t decide which way to go, say “How about you write a test for the feature and then try to modify the code so it makes the test pass. This way, we’ll see quickly which way works better”

3. Write some tests yourself. Make sure they run and if they fail, talk to the guy who made them fail. Help them to fix the problem. The bad solution would be to ridicule them, or punish them. Everyone makes mistakes. The tests are there to help to make less mistakes.

If the tests become another problem in the daily routine, They won’t generate enough positive energy for anyone to bother. Result: Failure.

If you meet resistance, ask yourself: What problem do I solve? Or am I just being religious? Do I test for the sake of testing? Or do the tests make everything better for everyone? Do they reduce stress or increase stress? Why?

All these questions will have different answers depending on your specific situation and the answers will help you to find your solution. Paper can’t think, you can. If some rule (out of a book or from your boss) just doesn’t make sense, then it’s your turn to come up with a solution.

There is just one thing to keep in mind and that’s the goal: Tests must help. Everything else will follow.

Testing the Impossible: Shifting IDs

7. October, 2009 at 09:38 | In Software | Leave a Comment
Tags: , , , , ,

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 at 19:34 | In Software | Leave a Comment
Tags: , , ,

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 at 16:58 | In Software | 2 Comments
Tags: ,

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 at 11:46 | In Software | Leave a Comment
Tags: , , ,

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 at 17:00 | In Software | Leave a Comment
Tags: , ,

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 at 12:54 | In Software | Leave a Comment
Tags:

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 at 19:04 | In Software | Leave a Comment
Tags: ,

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 at 11:08 | In Software | Leave a Comment
Tags: , ,

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 at 16:19 | In Software | Leave a Comment
Tags: , ,

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.

Next Page »

Blog at WordPress.com. | Theme: Pool by Borja Fernandez.
Entries and comments feeds.