Stubbing leveldb calls for testing

In notebook:
Work Notes
Created at:
2019-01-11
Updated:
2019-01-11
Tags:

There are three approaches I'm using right now

Testing for the batch operation

The great thing about this, is that you don't need to mock the leveldb module out, but just testing the you modules returns a list of operations:

  const ops = [
  {type: 'put', key: 'shorts', value: 'pants'},
  // etc
  ]

You just need to test that the array returned by your test subject contains what you need (using Jest here):

    test('list the notebooks', () => {
    let result = subject(simpleCase)
    let putCommand = R.find(
      R.propEq('key', 'tags:tag001:nbook001:note001'),
      result
    )
    expect(putCommand).toMatchObject({
      type: 'put'
    })
  })

You can also stub out the batch method and test how it was called

  const batch = jest.fn()
batch.mockReturnValue(Promise.resolve('inserted'))
const db = { get, batch, del }

jest.mock('./utils/db')
const getDb = require('./utils/db')
getDb.mockReturnValue(db)


// ... later

test('inserts the note', () => {
  expect(batch.mock.calls[0][0]).toContainEqual({
    type: 'put',
    key: 'noteid',
    value: {
      nbook: { name: 'nbookname' },
      note: { meta: { title: 'notetitle' } }
    }
  })
})

Stubbing the get method

The get and put methods are easier to stub out, because they are just asynchronous function calls or promises

  const get = jest.fn()

get.mockImplementation(r => {
  return new Promise((res, rej) => {
    if (r === 'nobook:new_nbookid') {
      rej('not found')
    } else {
      res({ updated_at: '101' })
    }
  })
})

const db = { get, batch, del }

jest.mock('./utils/db')
const getDb = require('./utils/db')
getDb.mockReturnValue(db)

Using memdown for more complex scenarios, e.g. stubbing createKeyStream

I've been experimenting with creating my own Readable stream provider and using it for stubbing methods like createReadStream.

from2 is a great package for doing this, but finally I realised that I should not go that far.

Instead, you can just use memdown for a temporary leveldb store.

  const db = levelup(encode(memdown()))

const data = [
  { type: 'put', key: 'tags:atag1', value: 'pants' },
  { type: 'put', key: 'tags:atag2', value: 'pants' },
]
db.batch(data, err => {
  if (err) {
    console.log('Ooops!', err)
  }
})
jest.mock('./utils/db')
const getDb = require('./utils/db')
getDb.mockReturnValue(db)

To make sure it mirrors the encoding used by the "real" leveldb store, you have to pass the memdown store into encoding-down