Spock It Like You Mean It!

So here we go.. Yesterday night we hacked our way into The Ancient Database where besides the data about ancients themselves, we found incredible stats about many different living species of all the planets ancients traveled to.

So what do we do now? Well, we need a way to query/read this data. So we asked Sam to develop a reader ( ancients called it a ‘DAO’ ) to find a number of living species on each planet since the “beginning of time”.

So she did, and called this “species reader” a ‘SpeciesDao’.

We, of course, trust Sam. But in ancient technology, trust is always based on challenging the assumption by testing all the possible permutations. So we are calling Mr. Spock to help us out…

Sam of course used a well known ancient technique ( code name: Spring Framework ) to create a “species reader”, so Mr. Spock will use all the goodies of this technique to inject all the dependencies ( e.g. data source, a reader itself, etc.. ) for a test.

Since Mr. Spock is a peaceful inhabitant from a planet Simplicity, it speaks a simple language called Groovy. This language is unique in a way that living beings of all the galaxies can pick it up and understand it within minutes, which by the way makes it a perfect choice for various intergalactic conferences.

To start off, we’ll tell Mr. Spock where to look for all the dependencies:

@ContextConfiguration( locations = [ "classpath:/conf/test-context.xml", "classpath:/conf/persistence-context.xml" ] )

The data we are going to test against will be in a form of DbUnit datasets, which by the way can be XML based, Excel based or even YAML based. In this example we are going to focus on planets of two galaxies that peak our curiosity the most: Milky Way and Pegasus.

  // Species of the Milky Way Galaxy by Planet
  static final P3X_888_SPECIES_DATASET = "/dataset/stage/p3x-888-species.xml"
  static final DAKARA_SPECIES_DATASET = "/dataset/stage/dakara-species.xml"
  ....
 
  // Species of the Pegasus Galaxy by Planet
  static final ASURAS_SPECIES_DATASET = "/dataset/stage/asuras-species.xml"
  static final WRAITH_HOMEWORLD_SPECIES_DATASET = "/dataset/stage/wraith-homeworld-species.xml"
  ....

Since it is a test, we already know the exact number of living spices since the ‘beginning of time’ for each dataset. And we’d need to call the reader Sam wrote ( ‘SpeciesDao’ ) for each dataset and compare it with an expected number. So you see, lots of repetitive actions. But no worries humans! Spock has an elegant way to deal with all these repetitions, by using its ‘WHERE’ power:

    where:
 
             planet                        |          numberOfSpeciesFound
 
      // Milky Way Galaxy
      P3X_888_SPECIES_DATASET              |                  888
      DAKARA_SPECIES_DATASET               |                 123804
      HELIOPOLIS_SPECIES_DATASET           |                   7
      .....

That’s neat. The only problem is if the number does not match ( yes, even Sam has bugs ) for example for planet Dakara, Mr. Spock will tell us that [ "something did not match up.." ], but will forget to mention that “it” did not match up specifically for a Dakara planet. And if we have thousands of such planets, it’ll be extremely difficult to find the culprit. But again, humans, this is easily solvable by using a Mr. Spock’s secret power: The power of ‘@Unroll’!

  @Unroll("#planet should have #numberOfSpeciesFound species found")
  def "only living species since begining of time should be found"() { ... }

By annotating a test method with @Unroll, in case where a number of living species found did not match a number that we expected, Mr. Spock will say just that. For example for Dakara, it’ll now say: “Dakara should have 123804 species found”, while also telling us the actual number that was found to compare. Pretty handy!

One last touch before we can fully trust Mr. Spock.. The way the ancient technology ( Spring ) was written, it won’t allow to inject collaborators ( e.g. data source ) statically before all the specifications / permutations. It can be tweaked to do that, but cmmon, who are we to tweak the ancients.. Instead we’ll tell Mr. Spock to do all the setup it needs only the first time a data source is injected:

    // setupSpec() cannot access Spring beans ( e.g. dataSource ), hence need to check it every time
    if ( ! databaseTester ) {
      databaseTester = new DataSourceDatabaseTester( dataSource )
    }

Now we are ready to roll. Let’s put all the pieces together and make sure Sam’s creation does what it’s supposed to:

 
@ContextConfiguration( locations = [ "classpath:/conf/test-context.xml", "classpath:/conf/persistence-context.xml" ] )
class FindNumberOfSpeciesTest extends Specification {
 
  // Species of the Milky Way Galaxy by Planet
  static final P3X_888_SPECIES_DATASET = "/dataset/stage/p3x-888-species.xml"
  static final DAKARA_SPECIES_DATASET = "/dataset/stage/dakara-species.xml"
  static final HELIOPOLIS_SPECIES_DATASET = "/dataset/stage/heliopolis-species.xml"
  static final ASCHEN_PRIME_SPECIES_DATASET = "/dataset/stage/aschen-prime-species.xml"
  static final P4X_650_SPECIES_DATASET = "/dataset/stage/p4x-650-species.xml"
  static final VIS_UBAN_SPECIES_DATASET = "/dataset/stage/vis-uban-species.xml"
  static final PROCLARUSH_SPECIES_DATASET = "/dataset/stage/proclarush-species.xml"
  static final DAKARA_SPECIES_DATASET = "/dataset/stage/dakara-species.xml"
  static final HEBRIDAN_SPECIES_DATASET = "/dataset/stage/hebridan-species.xml"
 
  // Species of the Pegasus Galaxy by Planet
  static final ASURAS_SPECIES_DATASET = "/dataset/stage/asuras-species.xml"
  static final WRAITH_HOMEWORLD_SPECIES_DATASET = "/dataset/stage/wraith-homeworld-species.xml"
  static final SATEDA_SPECIES_DATASET = "/dataset/stage/sateda-species.xml"
  static final DAGAN_SPECIES_DATASET = "/dataset/stage/dagan-species.xml"
  static final LORD_PROTECTORS_SPECIES_DATASET = "/dataset/stage/lord-protectors-species.xml"
  static final M7G_677_SPECIES_DATASET = "/dataset/stage/m7g-677-species.xml"
  static final ATHOS_SPECIES_DATASET = "/dataset/stage/athos-677-species.xml"
 
  static final THE_BEGINNING_OF_TIME = Date.parse( "yyyy-M-d", "1979-01-01" )
 
  @Autowired
  SpeciesDao speciesDao
  @Autowired
  DataSource dataSource
 
  @Shared IDatabaseTester databaseTester
 
  @Unroll("#planet should have #numberOfSpeciesFound species found")
  def "only living species since the beginning of time should be found"() {
 
    when: stageTestData planet
 
    then: speciesDao.findNumberOfSpeciesLivingSince( THE_BEGINNING_OF_TIME ) == numberOfSpeciesFound
 
    where:
 
             planet                        |          numberOfSpeciesFound
 
      // Milky Way Galaxy
      P3X_888_SPECIES_DATASET              |                  888
      DAKARA_SPECIES_DATASET               |                 123804
      HELIOPOLIS_SPECIES_DATASET           |                   7
      ASCHEN_PRIME_SPECIES_DATASET         |                 2423984
      P4X_650_SPECIES_DATASET              |                  2600
      VIS_UBAN_SPECIES_DATASET             |                   0
      PROCLARUSH_SPECIES_DATASET           |                 8869346
      DAKARA_SPECIES_DATASET               |                  5672
      HEBRIDAN_SPECIES_DATASET             |                   67
 
      // Pagasus Galaxy
      ASURAS_SPECIES_DATASET               |                  823
      WRAITH_HOMEWORLD_SPECIES_DATASET     |                 62634
      SATEDA_SPECIES_DATASET               |                  327
      SATEDA_SPECIES_DATASET               |                   0
      DAGAN_SPECIES_DATASET                |                  777
      LORD_PROTECTORS_SPECIES_DATASET      |                  8786
      M7G_677_SPECIES_DATASET              |                  4739
      ATHOS_SPECIES_DATASET                |                3767822
 
  }
 
  def stageTestData = { dataSetLocation ->
 
    dataSource != null
    speciesDao != null
 
    // setupSpec() cannot access Spring beans ( e.g. dataSource ), hence need to check it every time
    if ( ! databaseTester ) {
      databaseTester = new DataSourceDatabaseTester( dataSource )
    }
 
    databaseTester.setDataSet( new FlatXmlDataFileLoader().load( dataSetLocation ) )
    databaseTester.onSetup()
  }
}

Spock away Humans!

2 comments

  1. Пишите еще!

  2. I found this while searching for informations on testing with the Spring framework. I wish all tutorials were as funny while still being informative. Keep up the good work!

tell me something...
  1. (required)
  2. (valid email - optional)
  3. Captcha
  4. (required)