Lifecycles of Test Data

10 November 2017

Managing data created by tests can be a bit of work. The environments and frameworks we allow ourselves can help us or sometimes hinder our efforts.

Complete Isolation

Building test data from scratch every time in isolated storage is best. That’s why unit testing with mocks and data in memory is so great, and why it’s worth the trouble to use a fresh new database for every test run. Grails helps us by using an in-memory H2 database.

Shared, But Immutable

If you must share test data, confirm its expected state before testing with it and NEVER modify it. Race conditions between threads changing data are bad, and races between developers or build servers changing data are especially bad as well, even with only 2 builds. It’s embarrassing and limiting to have a project that can be built by only one person (or build server) at a time, and having any more would cause failures.

Shared Randomized Data

For the tests that must modify data that is shared, create that data in a random fashion, and only make changes to the data created by the test. Try to clean it up at the end, but if you don’t that’s mostly OK, because you’ll never try to reuse it again. Regardless if you try to clean up or not, you’ll still need to write the tests to assume there’s unexpected data in case a cleanup failed, or another test suite is being run concurrently. This provides opportunity for the tests to be clearer about what they really want to say. Instead of saying, "There are 2 records, the first is 'Joe' and the second is 'Bob'", maybe the test should say, "There’s at least 1 record, I see the 'Bob64524' record I just added somewhere in the list, and all the other records meet my search criteria."

Extra time spent being careful about your test data pays back in the end when you’re trying to work fast, rely on your tests, and won’t have time to deal with undue sporadic failures.


Alternate Constructors in Groovy

14 September 2017

There are a variety of ways to construct objects in Groovy. We were looking for something more dynamic where we could parameterize the object to be constructed.

We found a couple different things that worked to instantiate an object:

import groovy.transform.*

@ToString
class MyClass {
    String first
    String last
}

def map = [first: 'f', last: 'l']

// instantiate from literal class reference
println new MyClass(map)
println MyClass.newInstance(map)
println (map as MyClass)
println map.asType(MyClass)

// instantiate from a variable reference the class
def clazz = MyClass

println map.asType(clazz)
// println (map as clazz) // 'as' operator doesn't like 'clazz' variable
println clazz.newInstance(map)


Groovy Map Minus

06 June 2017

Groovy is relatively intuitive for me: I can usually guess a method or operator and find it works as I had guessed.

Removing a key/value by the key from a Map conveniently eludes me though. I always guess that I should be able to minus a key from a Map to produce a new Map with that entry /value removed. That doesn’t work, so I needed to invent my own. I still not sure how to practically apply it everywhere though, or if it’s even worthwhile. In practice, I always end up writing the findAll inline.

@Category(Map)
class MapMinus {
    Map minus(Collection keys) {
        this.findAll { k, v -> ! (k in keys) }
    }
    Map minus(Object key) {
        this - [key]
    }
}

use (MapMinus) {
    assert [b: 2, c: 3,] == [a: 1, b: 2, c: 3,] - 'a'
    assert [b: 2, c: 3,] == [a: 1, b: 2, c: 3,] - ['a']

}


All the Posts

November 2017

September 2017

June 2017