Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace method in tested file #339

Closed
davidbarton opened this issue Feb 14, 2018 · 6 comments
Closed

Replace method in tested file #339

davidbarton opened this issue Feb 14, 2018 · 6 comments

Comments

@davidbarton
Copy link

Hello, I am not sure if this feature is missing or I am just missing some piece of documentation but I am unable to replace the method inside the tested file. Here is the example what I am trying to accomplish.

// lib.js
export function addOne(x) {
  return x + 1
}

export function getTwo() {
  return addOne(1)
}
// test.js
import td from 'testdouble'

describe('Import test', function () {
  let libDouble, lib
  beforeEach(function () {
    libDouble = td.replace('./lib.js')
    lib = require('./lib.js')
  })

  afterEach(function () {
    td.reset()
  })

  it('tests getTwo', async function () {
    lib.getTwo()
    td.verify(libDouble.addOne(1))
  })
})

As you can see I am trying to replace and verify addOne(1) was called when both functions are imported from same file. I know about this being possible with babel-plugin-rewire but did not manage to get it running with td.replace(). Perhaps there is a missing piece of documentation which should say this is not possible or example how to achieve it or I just can't find it.

@searls
Copy link
Member

searls commented Feb 14, 2018

td.replace()'s module replacement is written to replace the dependencies of the module under test (lib.js), but it doesn't look like lib.js depends on anything. So what you're trying won't work, because it doesn't make sense to fake out lib.js and then immediately load it—what would you expect to get back?

More broadly, trying to mock something on the subject under test is considered an anti-pattern ("contaminated test subject"), because it reduces confidence in the veracity of the thing being tested. I'd call it a special subtype of a partial mock. My advice would be to keep the dependencies of the subject in separate modules so the delineation is crystal clear to the test and to readers.

Of course, ignoring this advice, you can accomplish what you want with:

const lib = require('./lib.js')
td.replace(lib, 'addOne')

And only addOne will be faked.

@searls searls closed this as completed Feb 14, 2018
@davidbarton
Copy link
Author

Thanks for your response. I understand what you are trying to say and will probably have to rewrite my code. However, I was trying to make the example work and it still fails. Actually the td.replace(lib, 'addOne') was the first thing I tried and it failed me. Here is the full example. Do you know what might be wrong?

// lib.js
function addOne(x) {
  return x + 1
}

function getTwo() {
  return addOne(1)
}

module.exports = {
  addOne: addOne,
  getTwo: getTwo
}
// test.js
var td = require('testdouble')
var lib = require('./lib.js')

describe('Import test', function () {
  afterEach(function () {
    td.reset()
  })

  it('tests getTwo', async function () {
    var addOneDouble = td.replace(lib, 'addOne')

    lib.getTwo()
    td.verify(addOneDouble(1))
  })
})
{
  "dependencies": {
    "mocha": "^5.0.0",
    "testdouble": "^3.5.0"
  }
}
➜  td ./node_modules/.bin/mocha test.js                                 13:00:37


  Import test
    1) tests getTwo


  0 passing (16ms)
  1 failing

  1) Import test
       tests getTwo:
     Error: Unsatisfied verification on test double `addOne`.

  Wanted:
    - called with `(1)`.

  But there were no invocations of the test double.
      at Object.fail (node_modules/testdouble/lib/log.js:27:11)
      at Object.exports.default [as verify] (node_modules/testdouble/lib/verify.js:50:19)
      at Context.<anonymous> (test.js:13:8)

@searls
Copy link
Member

searls commented Feb 14, 2018

Your issue is that you're assigning addOne and addTwo to an object on module.exports, and that's the reference you're replacing, but your internal reference is to the unreplaced original function, it just happens to have the same name.

This could be a good example of why not to mock things on the subject.

This would have worked:

const lib = {
  addOne: function (x) {
    return x + 1
  },
  getTwo: function () {
    return lib.addOne(1)
  }
}
module.exports lib

@davidbarton
Copy link
Author

Ahh ok, thank you for the answer.

@nohaibogdan1
Copy link

nohaibogdan1 commented Nov 22, 2018

If I replace the line : var addOneDouble = td.replace(lib, 'addOne')
with : var addOneDouble = td.func('addOne')

it shows there is no invocation of addOneDouble.
Why??

@searls
Copy link
Member

searls commented Nov 27, 2018

@nohaibogdan1 because td.func() would create a test double function, but would not set it anywhere other than the local scope of the test, so nothing would be invoking it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants