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

Sinon causing issues again #304

Closed
altano opened this issue Jun 8, 2014 · 51 comments
Closed

Sinon causing issues again #304

altano opened this issue Jun 8, 2014 · 51 comments
Labels

Comments

@altano
Copy link

altano commented Jun 8, 2014

The new version of Sinon is throwing webpack for a loop again. This code:

    function makePublicAPI(require, exports, module) {
        module.exports = sinon;
        sinon.spy = require("./sinon/spy");
        sinon.spyCall = require("./sinon/call");
        sinon.behavior = require("./sinon/behavior");
        sinon.stub = require("./sinon/stub");
        sinon.mock = require("./sinon/mock");
        sinon.collection = require("./sinon/collection");
        sinon.assert = require("./sinon/assert");
        sinon.sandbox = require("./sinon/sandbox");
        sinon.test = require("./sinon/test");
        sinon.testCase = require("./sinon/test_case");
        sinon.match = require("./sinon/match");
    }

    if (isAMD) {
        define(makePublicAPI);
    } else if (isNode) {
        try {
            formatio = require("formatio");
        } catch (e) {}
        makePublicAPI(require, exports, module);         <========
    }

Is causing this warning:

WARNING in ./~/sinon/lib/sinon.js
Critical dependencies:
365:22-29 require function is used in a way, in which dependencies cannot be statically extracted
 @ ./~/sinon/lib/sinon.js 365:22-29

It's not obvious to me how to work around this. Any of you have any bright ideas?

(the line number indicated in the error, 365, is clearly wrong, and the line with the arrow is actually the offending code)

@fresheneesz
Copy link
Contributor

I would guess that webpack is detecting that the require function is being passed into makePublicAPI, and is saying that it doesn't know how to handle that case.

To support this case, webpack would need to locate functions that require is passed into and parse those using the correct parameter name to find the requires. Its certainly doable, are you up to the task?

The other option is to change sinon so that its not using this particular pattern to support both AMD and CommonJS.

@jhnns
Copy link
Member

jhnns commented Jun 9, 2014

sinon is our special friend... ^^

@sokra
Copy link
Member

sokra commented Jun 10, 2014

😢 just use UMD...

@altano
Copy link
Author

altano commented Jun 12, 2014

Oh dear, I took a look at what it might take to switch it over to UMD and stumbled on this. What on earth? Not sure I'm going to want to tackle that immediately...

Anyone know a good SinonJS alternative? I've found the API hard to wrap my head around anyway, with all the extremely similar but not exactly the same functionality.

@fresheneesz
Copy link
Contributor

Oh hm, Sinon.js is a test framework. I actually spent quite a bit of time writing deadunit, which provides a test API and nice pretty test output for node.js and the browser. It doesn't have any functionality for creating spies, but that's ok because using test spies is bad practice

@mzgoddard
Copy link
Contributor

I bumped into this yesterday. For now I'm using the script-loader to work around this.

@fresheneesz Sinon.js isn't a test framework, it's a library to be used in testing. I'm not sure how that stack-overflow question demonstrates spies being a bad practice, it looks more like a conversation about a design that is hard to test.

@fresheneesz
Copy link
Contributor

Ah, my mistake.

Well, the chosen answer in that stackoverflow question describes that test spies mean you're not doing black-box testings. The kind of so-called "white-box" testing that uses spies and mocks is orders of magnitude more work to write, not only because there is so much more test-code to write, but any time the implementation is changed, you must also change the tests (even if the interface remains the same). And this kind of testing is also less reliable than black-box testing, because you need to ensure that all that extra test code is correct, and while you can trust that black-box unit tests will fail if they don't match the interface, you can't trust that about code overusing mocks because sometimes the test doesn't even test much real code - only mocks. If the mocks are incorrect, the likelyhood is that your tests will succeed, but your code is still broken.

Anyone that has experience with white-box testing can tell you they're a pain in the ass to write and maintain. Coupled with the fact that they're less reliable, white-box testing is simply far inferior in most cases.

@mzgoddard
Copy link
Contributor

That as an argument for how spies and mocks can be misused makes way more sense.

@jhnns
Copy link
Member

jhnns commented Jun 22, 2014

I know this is off-topic, but personally I use Grey-box-testing. I know "something" about the implementation, but I don't really test everything "behind the scenes".

A common use-case for spies is that you have several functions calling a particular function. By just testing this particular function and by using spies in all other cases I can keep all tests simple and expressive. Otherwise I needed to duplicate all tests.

Testing something "behind the scenes" should be an exception, but sometimes it actually leads to cleaner and more maintainable tests.

@fresheneesz
Copy link
Contributor

You always want to test your interface. If you're substituting testing your public interface with testing some inner function, I'd say that's the wrong move, no matter how significant that inner function is. If you have duplication in your tests, you can always take advantage of that symmetry - factor it out into common functions.

@luxx
Copy link

luxx commented Jul 15, 2014

May or may not be helpful, but I ran into this issue with karma + sinon + sinon-chai. I was requiring sinon directly, however using karma-sinon-chai eliminated the problem, probably because of how it requires the dependencies.

If you are unit testing and your object under test has a command defined that sends a message to another object, but has no other visible side effects, you do need to assert the message was sent, which is exactly what sinon allows you to do. Recommended reading: https://speakerdeck.com/skmetz/magic-tricks-of-testing-railsconf

@binarykitchen
Copy link

Any news on this one?

@mzgoddard Would you mind to share your script-loader here?

@elsigh
Copy link

elsigh commented Apr 6, 2015

Want.

@altano
Copy link
Author

altano commented Apr 8, 2015

I'll leave this here for others... if you're depending on libraries for testing that use Sinon, you can't control what version of Sinon the library uses. So you might run into a problem where the version it uses can't even be used with script-loader. To deal with this:

  • Download a release version of Sinon, e.g. http://sinonjs.org/releases/sinon-1.14.1.js
  • Turn on noParse for sinon: that means webpack will not parse the file, it won't be minified, require will not work in the file, etc.
  • Use NormalModuleReplacementPlugin to point require("sinon") to your local file instead of the NPM package.

Like so:

module: {
    noParse: [
        /sinon/
    ]
},
plugins: [
    new webpack.NormalModuleReplacementPlugin(/sinon/, __dirname + "/TestRunner/vendor/sinon-1.14.1.js")
]

You should not normally do this because the library you depend on might upgrade to a version of Sinon that isn't API compatible with your local copy, or you might depend on multiple packages that depend on varying versions of Sinon. This approach is totally circumventing both NPM and webpack. And for sure, NEVER use this approach in releasing code, only tests.

Perhaps once Sinon 2 is more prevalent, these issues will go away, but in the mean time...

@tomatau
Copy link

tomatau commented Apr 14, 2015

Right now I'm running webpack mocha tests over a babel loader and import sinon from 'sinon' works only when used in conjunction with sinon-2.0 in my packages.json:

"sinon": "git://github.com/cjohansen/Sinon.JS#sinon-2.0",

However this doesn't support sinon.useFakeTimers() which is very unfortunate.

Thanks @altano for your alternative although it looks like it may throw up a bunch of problems of it's own!

@kpdecker
Copy link
Contributor

For my own setup (babel, sinon, chai, sinon-chai) I had to make a few alterations to @altano's local copy solution:

For useFakeTimers:

import 'expose?lolex!lolex';

Also need to make sure that this is excluded from the babel loader as noParse doesn't seem to impact loader execution.

For the final bit, needed to make sure that sinon-chai was treated as normal, meaning slight modification to @altano's regexs:

  module: {
    noParse: [
      /\/sinon.js/
    ]
  },
  plugins: [
    new webpack.NormalModuleReplacementPlugin(/^sinon$/, __dirname + '/vendor/sinon.js'),
  ]

This is multiple levels of yuck but it seems to work for now :(

@danielkcz
Copy link
Contributor

How comes that sinon is working with browserify just perfectly? I just started using webpack for a new project (gulp, webpack, babel, mocha, karma), but issues like this are giving me a second thoughts if it's mature enough for a development.

For the record, I am not using sinon for any mocking or spying. I love its convenient anonymous spy which can be simply passed as a callback to my methods and then evaluate results with couple of assertions instead of making ugly function like this.

it('should...', () => {
    let called = 0;
    myMethod((data) => {
        if (called === 0) {
            expect(data.whatever).to.equal('test');
        }
        called += 1;
    });
});

With sinon I can it simply like this. It's much more readable with less hassle (and possible mistakes).

it('should..., () => {
    const spy = sinon.spy();
    myMethod(spy);
    expect(spy.firstCall).calledWithMatch({ whatever: 'test '});
});

Can you perhaps recommend me some other library that can do this?

@andrei-picus-hs
Copy link

I've created a fork of Sinon over at uberVU/Sinon.JS that doesn't include the AMD logic. Works perfectly with webpack.

@maikdiepenbroek
Copy link

@andrei-picus-hs You sir, are my hero!

@screendriver
Copy link

Same here. Is there an easy way to get Babel, Mocha, Chai, Sinon and Sinon-Chai running with webpack? I can't directly reference GitHub dependencies here so I can't use the @andrei-picus-hs repository unless it's deployed to npm. As well I am afraid that it will always behind the official Sinon Repo.

The Sinon 2.0 branch seems dead by the way.

@melbourne2991
Copy link

@screendriver I searched far and wide, doesn't seem like it - @andrei-picus-hs solution is your best bet:

devDependencies: {
    "sinon": "git+https://github.com/uberVU/Sinon.JS"
}

Using Sinon's 2.0 branch works as well, but doesn't include the util functions (like the fakeServer)

@screendriver
Copy link

Unfortunately as I mentioned earlier: I can't directly reference GitHub dependencies here in my company.

In the meantime I switched away from webpack to SystemJS and jspm.

nathanstitt added a commit to openstax/tutor-js that referenced this issue Sep 8, 2015
Webpack has issues with the non-stardard setup that Sinon.js uses:
webpack/webpack#304
nathanstitt added a commit to openstax/tutor-js that referenced this issue Sep 8, 2015
Webpack has issues with the non-stardard setup that Sinon.js uses:
webpack/webpack#304
@heldr
Copy link

heldr commented Nov 2, 2015

It works for me just adding noParse rule on webpack and requiring the dist version instead.

Example:

import sinon from 'sinon/pkg/sinon';

@SpaceK33z
Copy link
Member

Thanks @heldr, that seems to work! In my webpack.config.js, I added this:

{
    module: {
        noParse: [
            /node_modules\/sinon/,
        ],
    }
}

@graingert
Copy link

Because of peerigon/legacy-loader#1 I've actually switched to using:

npm install --save-dev sinon@next

@ericdfields
Copy link

+1 for @heldr's technique

@jbcpollak
Copy link

I tried this:

module: {
  noParse: [
     /\/sinon\.js/,
  ],
  resolve = {
    alias: {
      sinon: 'sinon/pkg/sinon',
    },
  }
}

But I get errors like this:

ERROR in ./~/sinon/pkg/sinon-1.7.3.js
Module not found: Error: Cannot resolve 'file' or 'directory' ./buster-event-emitter in ..../node_modules/sinon/pkg
 @ ./~/sinon/pkg/sinon-1.7.3.js 248:30-63

ERROR in ./~/sinon/pkg/sinon-1.6.0.js
Module not found: Error: Cannot resolve 'file' or 'directory' ./buster-event-emitter in ..../node_modules/sinon/pkg
 @ ./~/sinon/pkg/sinon-1.6.0.js 250:30-63

ERROR in ./~/sinon/pkg/sinon-1.7.3.js
Module not found: Error: Cannot resolve 'file' or 'directory' ./define-version-getter in ..../node_modules/sinon/pkg
 @ ./~/sinon/pkg/sinon-1.7.3.js 251:23-57

ERROR in ./~/sinon/pkg/sinon-1.6.0.js
Module not found: Error: Cannot resolve 'file' or 'directory' ./define-version-getter in ..../node_modules/sinon/pkg
 @ ./~/sinon/pkg/sinon-1.6.0.js 253:23-57

ERROR in ./~/sinon/pkg/sinon-1.7.3.js
Module not found: Error: Cannot resolve module 'buster-core' in ..../node_modules/sinon/pkg
 @ ./~/sinon/pkg/sinon-1.7.3.js 263:13-35

I only depended on sinon 1.7.3, so I'm not sure where the references to 1.6.0 come from.

@ericdfields
Copy link

@jbcpollak move resolve outside of module:

{
  module: {},
  resolve: {}
}

@bcowgill
Copy link

+1 for @graingert

thanks, that's the only thing that has worked for me. I was getting out of memory garbage collection errors I've never seen before with the other methods!

oddui added a commit to oddui/webdriver-extension that referenced this issue Jun 5, 2016
The `watch` task lints and bundles the script and spec files if they
change. It also watches the bundled `app/test/bundle.js` to reload the
extension.

Sinon hacks to work with webpack.
see webpack/webpack#304

```
module: {
  noParse: [
    /node_modules\/sinon\//,
  ]
},
resolve: {
  alias: {
    sinon: require.resolve('sinon/pkg/sinon')
  }
}
```
@aaronjensen
Copy link

fwiw, we had better luck using:

import 'expose?lolex!lolex'
import 'script!sinon/pkg/sinon'

(replace with require/webpack aliases/loader config/etc if you want)

@slavik57
Copy link

@heldr solution worked =] you are the man!

bobwhitelock added a commit to bobwhitelock/react-base that referenced this issue Jul 25, 2016
Work around for issue discussed here:
webpack/webpack#304.
huy-nguyen pushed a commit to huy-nguyen/restaurant-menu-builder that referenced this issue Aug 2, 2016
- Note that `sinon` uses a pre-release version. The reason is that
  versions of `sinon` prior to 2.0 has trouble working with webpack
  (webpack/webpack#304). We have to make do
  with a pre-release version because 2.0 has not hit release status yet.

- Disable type checking for test-related packages/variables because such
  checking isn't very useful in test suites.
@optimatex
Copy link

thanks @graingert , only ur solution with npm install --save-dev sinon@next works for me

@ecrona
Copy link

ecrona commented Nov 13, 2016

I initially attempted to import sinon manually (evidently not needed) which caused this problem, so I just removed the import line at the top of the script and used it globally, and it seemed to work.

@timmyLan
Copy link

@heldr +1, works for me,THX

@graingert
Copy link

FYI sinon@next is very close to release: sinonjs/sinon#966

@shults
Copy link

shults commented Jan 12, 2017

  // webpack config here: 
  resolve: {
    root: [ path.resolve(__dirname) ],
    extensions: ['', '.ts', '.js'],
    alias: {
      sinon: `${__dirname}/node_modules/sinon/pkg/sinon.js`
    }
  },
  module: {
    noParse: [ /sinon\.js/ ],
    // ...
  }
// importing sinon from file.spec.ts
import * as sinon from 'sinon';

@graingert
Copy link

@shults just use sinon@next

Gregoirevda added a commit to Gregoirevda/hackjam.rxjs that referenced this issue Feb 23, 2017
See thread : webpack/webpack#304
Nice you added the sinon alias, but from what 'heldr' says, noParse needs to be added too.
This fixed my problem on windows node v 6.9
@aweiher
Copy link

aweiher commented Mar 22, 2017

If anyone comes here for this error in combination with karma and phantomjs:

PhantomJS 2.1.1 (Linux 0.0.0) ERROR
  ReferenceError: Can't find variable: require

Add Sinon as external in webpack config:

externals: ["sinon"]

(currently webpack 1.x and sinon 2.1.0 for me)

ormsbee pushed a commit to openedx/edx-platform that referenced this issue Jan 21, 2018
Running LMS Karma tests was causing the following error:

  TypeError: modules[moduleId] is undefined
  at /edx/app/edxapp/edx-platform/lms/static/course_experience/js/spec/Enrollment_spec.js:20

The root issue was sinon, as documented here:
  webpack/webpack#304

I tried multiple fixes suggested from that issue until I found one that
worked: webpack/webpack#304 (comment)
kevinjmann pushed a commit to kevinjmann/view-addon-study that referenced this issue Apr 22, 2018
rajr5 pushed a commit to rajr5/bemuse that referenced this issue Jun 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests