Monday, February 23, 2015

... about ugly UUID's in my tests



In my current project we use UUID's as id's for all aggregates. The good part of UUID's is that it's guaranteed to be unique. The ugly part is that it looks ... well ... ugly.

Writing tests with lots of UUID's clutters the test codebase and you lose focus on what it is you're testing.

A common use case when writing tests is that I want to assert that some id is the same as some expected id. For instance I want to test a filter - named VeryComplexFilter - that keeps users older than 40 and sorts them by age. The code is written in groovy:

Loading code....

I know. It's not the most compelling code, but lets continue for the sake of the example. My unit test creates a list of users and asserts the outcome of my filter. I'm using a data-driven test using spock because I want to test different inputs in the same test. Spock does a great job here.

Loading code....

In the where-block, I create the different input lists and define the expected  result. Each input list contains pairs of UUID-and-age tuples. In the when-block, each element of the input list is converted into a User object and handed to the filter under test. Finally in the then-block, the id's of the result list is compared against the expected results. When I run the test I get the following output:


You can see that the number of UUID's I have to create, really makes my test ugly. I could pregenerate them and then it would look like this:

Loading code....

It's the same test but I pregenerate the UUID's in a static list and refer to them from within the where-clause. However, what I really want, is to use some symbolic notation like a string that refers to a UUID. The test than would look much simpler:

Loading code....

In this where-block the input list contains a lists of strings like 'a-35' that I can refer to in the expectedResults. Now I can specify, for instance, that - provided with a list of users with ages 35, 52, 15, 79 and 41 - the filter must return the 2nd, the 5th and the 4th user. I do this by setting ...

  • the input list to "a-35", "c-52", "d-15", "e-79", "b-41" and 
  • the expectedResult to 'bce'
Every time I refer to uuid 'a' or 'b' or 'c', I want the system to remember what UUID I'm talking about. I can easily do that with the help of groovy's memoization:

Loading code....

I just call a closure named uuid that returns me the same UUID, every time I call it with the same input. Executing uuid.call('a') twice will always give me the same UUID. Executing uuid.call('b') gives me a new UUID.

What if I want to return something else instead of UUID's? I can do the same creating a user:

Loading code....

I can generalize this idea of creating something and return the same complex object, every time I call it with the same input parameter. Here's a method creating a complex object:

Loading code....

Here's how I call it and expect to get the same object for the same input value:

Loading code....

For this to work I need to be able to say: "This is how you create my complex object":

Loading code....

The above snippet registers a way to create the object and also passes the key that I can use to specify what I want. Once that's done, I can call it, like I did using the uuid closure call.

Loading code....

You can find a full gist here.

I don't know how far I'll take this trick in my tests. The fact that you can return the same - potentially complex - object and use some short symbol to reference it, helps a lot when trying to create some clear specification. However, there is some hocus-pocus going on, so it's not something I would apply everywhere... I think.

Greetings
Jan

No comments:

Post a Comment