Jez Nicholson (14) [Avatar] Offline
#1
Tests are reliant on specific data existing in DynamoDB, etc. Do you have any tips for creating the starting data? Squirt it straight into DynamoDB from the init.js? use a create function first? not always great to have tests reliant on other tests
Yan Cui (68) [Avatar] Offline
#2
My preference would be for each test to set up and tear down test data it depends on, via the "before" and "tear down" steps, as you said, it sucks to depend on other tests to bootstrap the test data. Unfortunately in this case I kinda coded myself into a corner with the test case for "get-restaurants"!

If I can do it again (without having to re-record a whole module) then I'd rewrite the test case for "get-restaurants" so that:

  • insert test data (i.e. restaurants) in the "before" step

  • validate that 8 restaurants (don't have to match the ones we added, but at least we're not dependent on the DDB being bootstrapped by something else outside of the tests

  • delete the test data in the "tear down" step


  • Similarly, for the "search-restaurants":

  • insert test data (i.e. restaurants) with some randomly generated theme in the "before" step

  • validate that we're able to search for the test data we inserted, by searching for that randomly generated theme

  • delete the test data in the "tear down" step
  • Jez Nicholson (14) [Avatar] Offline
    #3
    my eventual solution was using https://github.com/aexmachina/factory-girl and adding a DynamoDbAdapter.

    My factory-girl models are really skinny, as DynamoDB only need know the table name to create and the primary key for deleting.

    /tests/models/Player.js
    class Player {
      tableName() { return 'players-dev' }
      key() { return { playerId: this['playerId'] } }
    }
    
    module.exports = Player
    


    /tests/factories/randomData.js
    const adapter = new(require('./DynamoDBAdapter'))()
    const chance = require('chance').Chance()
    const factory = require('factory-girl').factory
    factory.setAdapter(adapter)
    const Event = require('../models/Event')
    const Player = require('../models/Player')
    
    // give full stack trace
    process.on('unhandledRejection', (reason, p) => {
      console.log('Unhandled Rejection at: Promise', p)
      console.log('reason:', reason)
    })
    
    ///
    // random data generation factories (in alphabetical order)
    // NB: most must be linked to a Player, e.g.
    // $ player = yield factory.create('player')
    // $ yield factory.create('event', { playerId: player.playerId })
    //
    
    factory.define('player', Player, {
      playerId: factory.chance('guid'),
      cohort: 'A',
      hero: {},
      name: factory.chance('last'),
      immunity: 0,
      coins: factory.chance('integer', {
        min: 0,
        max: 1000
      }),
      empireSize: factory.chance('d20')
    })
    
    factory.define('event', Event, {
      eventId: factory.chance('guid'),
      // playerId: must be passed in
      events: factory.chance('sentence')
    })
    
    exports.factory = factory
    


    /tests/factories/DynamoDBAdapter.js
    const factory = require('factory-girl')
    const AWS = require('aws-sdk')
    AWS.config.region = 'eu-west-1'
    const dynamodb = new AWS.DynamoDB.DocumentClient();
    const co = require('co')
    
    class DynamoDBAdapter extends factory.ObjectAdapter {
    
      save(model, Model){
        return Promise.resolve(
          co(function*() {
            yield dynamodb.put({
              TableName: model.tableName(),
              Item: model
            }).promise()
          }).then(function(data) {
            true
          }, function(err) {
            console.error(err.stack)
          })
        ).then(() => model)
      }
    
      destroy(model, Model) {
        return Promise.resolve(
          co(function*() {
            yield dynamodb.delete({
              TableName: model.tableName(),
              Key: model.key()
            }).promise()
          }).then(function(data) {
            true
          }, function(err) {
            console.error(err.stack)
          })
        ).then(() => true)
      }
    }
    
    module.exports = DynamoDBAdapter
    
    

    tests can then create and clean up after themselves...

    /tests/test_cases/getPlayer.js
    const co = require('co')
    const expect = require('chai').expect
    const init = require('../steps/init').init
    const whenWe = require('../steps/whenWe')
    const given = require('../steps/given')
    const tearDown = require('../steps/tearDown')
    const factory = require('../factories/randomData').factory
    
    describe(`Given a player`, co.wrap(function*() {
      let asUser
      let player
    
      before(co.wrap(function*() {
        yield init()
        asUser = yield given.authenticatedUser()
        player = yield factory.create('player')
      }))
    
      describe(`When we get the player`, co.wrap(function*() {
        let res
    
        before(co.wrap(function*() {
          yield init()
          res = yield whenWe.getPlayer(player.playerId, asUser)
        }))
    
        it(`Should succeed`, co.wrap(function*() {
          expect(res.statusCode).to.equal(whenWe.GET_OK)
        }))
    
        it(`Should have a name`, co.wrap(function*() {
          expect(res.body).to.have.property('name')
        }))
      }))
    
      after(co.wrap(function*() {
        yield tearDown.deleteUser(asUser)
        yield factory.cleanUp()
      }))
    
    }))
    ....