Rahman Kalfane
Rahman Kalfane

Reputation: 1717

Mocking filesystem with jasmine and node.js

I'm having trouble with testing my file access with jasmine. I am writing a simple watcher that registers a callback with require('fs').watch and emits an event containing the name of the file, nothing fancy here.

However when I try to write tests that mock the fs module I have several problems.

Here is my Watcher class (CoffeeScript ahead)

class Watcher extends EventEmitter
  constructor: ->
    @files = []

  watch: (filename) ->
    if !path.existsSync filename 
      throw "File does not exist."
    @files.push(filename)
    fs.watchFile filename, (current, previous) ->
      this.emit('file_changed')

And here are my tests :

it 'should check if the file exists', ->
  spyOn(path, 'existsSync').andReturn(true)
  watcher.watch 'existing_file.js'
  expect(path.existsSync).toHaveBeenCalledWith 'existing_file.js'

This one works well and passes without any problem but this one fails completely, I am not sure if I am passing correctly the arguments.

it 'should throw an exception if file doesn\'t exists', ->
  spyOn(path, 'existsSync').andReturn(false)
  expect(watcher.watch, 'undefined_file.js').toThrow()
  expect(path.existsSync).toHaveBeenCalledWith 'undefined_file.js'

And the last one gives me and odd '([Object] does not have a method emit)' which is wrong.

it 'should emit an event when a file changes', ->
  spyOn(fs, 'watchFile').andCallFake (file, callback) ->
    setTimeout( ->
      callback {mtime: 10}, {mtime: 5}
    , 100)
  spyOn(path, 'existsSync').andReturn(true)
  watcher.watch 'existing_file.js'
  waits 500
  expect(watcher.emit).toHaveBeenCalledWith('file_changed')

For the second problem I just wrapped my function call in a closure and it worked but I really need to understand why when running my tests, the this context is totally messed up.

Upvotes: 3

Views: 3477

Answers (2)

Vad
Vad

Reputation: 4099

You can use memfs for filesystem mocking.

Upvotes: 0

Peter Lyons
Peter Lyons

Reputation: 146114

See this question

I think you need to do:

expect(-> watcher.watch 'undefined_file.js').toThrow 'File does not exist.'

Which defines an anonymous function that the expect matcher can invoke during the actual test run, as opposed to during test definition time.

For your second issue, you can only call toHaveBeenCalled on a jasmine spy object, not any arbitrary function. You can just wrap the function with doing

spyOn(watcher, 'emit').andCallThrough()

See the jasmine API docs on Spy.andCallThrough()

Upvotes: 2

Related Questions