-
Notifications
You must be signed in to change notification settings - Fork 29
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
refactor(spy): removes reset method #74
Conversation
I miss
|
Just create a new instance of let spy = chai.spy()
let foo = FooFactory.create(spy)
foo.someFunc()
expect(spy).to.have.been.called.twice()
expect(spy).to.have.been.called.with('bar')
expect(spy).to.have.been.called.with('baz')
spy = chai.spy()
foo = FooFactory.create(spy)
foo.someFunc2()
expect(spy).to.have.been.called.once()
expect(spy).to.have.been.called.with('bar') Better way: describe('....', function(){
let foo, spy
beforeEach(()=>{
spy = chai.spy()
foo = FooFactory.create(spy)
})
It('....', () => {
foo.someFunc()
expect(spy).to.have.been.called.twice()
expect(spy).to.have.been.called.with('bar')
expect(spy).to.have.been.called.with('baz')
})
it('....', () =>{
foo.someFunc2()
expect(spy).to.have.been.called.once()
expect(spy).to.have.been.called.with('bar')
})
}) |
Thanks for the response. But the thing is that
|
So, probably this feature - #75 may be more useful for you. As you will be able to check |
This is a pretty lowsy regression. before(() => {
chai.spy.on(ga, 'trackEvent');
});
beforeEach(() => {
ga.trackEvent.reset();
}); Additionally Poor choice, and it should have had more community feedback before merging (as it was created and merged on the same day). I'll be reverting that change in |
Update: please check #38 for details Update 2: you need to change your code to this: beforeEach(() => {
chai.spy.on(ga, 'trackEvent');
});
afterEach(() => {
chai.spy.restore(ga, 'trackEvent');
}); This is the more propere way to do it. |
I understand that, and it's still a regression. Spies don't have to be immutable in that way. There's no discernible advantage to it other than saying "hey look we're immutable". Your solution is to write more code where none was needed before. The cost-benefit ratio is terribly out of whack, and your only community feedback on this regression is negative. I'm gonna maintain my disagreement and the fork that reverts the regression. |
@shellscape I don't think that example above pushes you to write more code :) we received "negative" feedback only from you and @alexmnv . Both cases are solvable without By the way, how do you restore |
@shellscape also your statement about immutability is not correct. It was done by purpose which I explained in #35 (comment) and it's not just |
Disagree again. Your perspective doesn't take into account others' perspectives. As evidenced by:
You're forgetting that not everyone works like you work. You're forgetting that flexibility in a testing environment is paramount. Your reasoning is entirely based on your point of view. I'm sorry man, but while you may think the way you prefer to work is the best way to go, there are plenty of others that disagree. If we were talking webpack, react, chrome, etc in projects that are too large to accommodate insane layers of flexibility - you might have a point. But this a an effin small lib, and you're mandating your patterns onto others. I prefer flexibility and letting people work how they want to work, even if that means completely messing with a spy to do some crazy stuff that you can't imagine. The argument that it adds complexity just doesn't hold water. |
I see your point. Thanks :) |
I entirely agree with @shellscape, this change means that we are not going to be able to stay on this library. We use spies to mock out external dependencies at import, so "create [a] new one" isn't an option. It would make our tests take ~10 times as long to reinitialize every file for every individual test case where resetting makes this trivial. Spies are, by their nature, NOT immutable as every call made to them mutates them by recording interactions. This change doesn't help your users, it doesn't help the library, and it makes the library significantly less useful. |
@stalniy Just because you don't receive negative feedback quickly doesn't make the change a good one. We don't all have the luxury of operating on the bleeding edge for all of our dependencies. The only feedback you've gotten on the change has been negative, that should tell you something. |
@keithamus what do you think about this? |
I’m pretty sure that it won’t make your tests bigger :) this is a strange resilience to refactor own code and desire to use the latest version ;) Do you know that you can register global So, probably you already do something like beforeEach(() => /*very complex spy setup */ ) Then the only think you should do is to add globally
Don’t take me wrong. I don’t want to make your life harder. What I want is to see short, easy understanble tests in my projects, that’s why such change was suggested. I believe that if you use P.S.: if other guys from the team think it make sense to add |
I think sandboxes negate the need to restore spies. As @stalniy points out Spies should not need to be reset - if you have code that resets spies in the middle of a test it could be a sign that you need to split the tests out further. The point of Chai and all plugins isn't just to facilitate testing how you want to write tests, it is also to guide you into a way of writing better tests. Having I'll add to that - @AdamMcCormick if you show us your test code where you're using |
I don't have skin in this game any longer, as I've moved towards jest and ava in the time since I was active on this thread. chai is of course not Jest nor Sinon, but it would appear those two fairly well established projects disagree with this stance, and chai is swimming against the current (which may or may not be goal; to differentiate itself) refs: |
Look I don't need your help to know how to refactor my code around an unnecessarily opinionated framework. It's just disappointing that you aren't listening to the users of your library. If I'm going to spend the man hours to refactor my code I'm going to move to a library that makes my tests work the way I have architected them. @stalniy I make full use of the test life cycle and I don't use @keithamus You're providing a library for testing, I'm using your code because I like the readability of chai assertions, not because I need to be taught how to write tests. I've been doing professional development a long time, and if there's one thing I've learned it's that telling your users how they should work is the easiest way to make your product fail. I'll add that - @keithamus if you really want to see my tests I'm happy to share them, but your definition of "better" is not so universal as you seem to believe |
@shellscape afterwards there are quetions and issues like this jestjs/jest#5143
@AdamMcCormick could you please show me your tests? I'd like to see few use cases, make benchmarks and probably write an article about how to migrate from using |
Here's a stripped-down test setup from one of our simplest tests (only 2 mocked dependencies), https://gist.github.com/AdamMcCormick/618bf8e9028c9940d7b41fccd06d9294. We have classes with as many as 10 or fifteen dependencies so it only gets more complex. Add a few hundred tests and using beforeEach for mock injection becomes untenable. I'm sure it could be mocked out using your sandboxes, I vehemently disagree that it should be. But by all means, write your blog about knowing better than the rest of the software community, I'm sure it will change everyone's minds. |
@stalniy You should read the link you posted, it's actually based on the same source where I originally got my pattern from. In particular, look at this comment jestjs/jest#5143 (comment) |
@AdamMcCormick thanks for sharing your tests. I agree with what you're saying, there is a performance hit for some code where the setup time can become untenable to repeat in each test. The code you have is heavily dependant on externalities that you need to mock out. This is typical of a lot of JavaScript, because there is no clean pattern for dependency injection. Often when I see code like this I push for architectural changes to declare dependencies as function arguments, as it makes the testing path a lot smoother - good examples of this are Angular's But let's get to the brass tacks: we're happy to listen to our users but also we're not going to implement every feature a user asks for. For example we've had plenty of requests in Chai core for a Given your set up, and how you are writing complex mocks, I think you might be better off using Sinon + Sinon-Chai over Chai Spies. Sinon has mocks and matchers and built in faking of XHR, you can assert on complex layering of calls, all the stuff it looks like (from the brief test example you've shown us) your code might benefit from. Chai Spies will never do everything that Sinon does. To quote our readme (emphasis mine):
Chai Spies is intentionally made to be a simple as possible. If you want something more complex, we fully encourage you to work with Sinon instead! |
@keithamus That's exactly what I like about your library (or liked before the change) you weren't opinionated about how my code and tests had to be written and since I'm working on existing enterprise systems that meant the test framework could be integrated without massive refactoring efforts. I find it interesting that you think reset is somehow more complex that sandbox/restore since it took something like 9 lines of code in your baseline to get reset (27cbb16) and hundreds for sandboxing (b11aeeb). If you want the "most basic function spy ability" |
It's not about the lines of code we ship, it's about complexity of our API and subsequently the intuitiveness of the library and the amount of support/learning it requires. Sandboxes are an incredibly useful and powerful piece of functionality, and are sufficiently complex enough that for our users to implement would be virtually impossible (without rewriting the whole lib). The cost of complexity in the code is worth it because of the intrinsic value it provides.
But look, if you really want |
@keithamus I find your argument about the harm to users unconvincing as your users don't seem to agree with the assertion. The functionality is to take a spy and, in place and without reassignment so as to maintain all references, clear all recorded activity. That doesn't clobber any prototypes (this is a state mutation, no more or less), it does a very specific thing (reset state to the initial proxy https://github.com/chaijs/chai-spies/blob/master/lib/spy.js#L225), and it's far clearer than sandbox and restore to understand and write around. I also don't see value in changing the name from the name that matches all the other libraries, but whatever. The static method is literally the five lines at https://github.com/chaijs/chai-spies/blob/master/lib/spy.js#L225 with a conditional around it to check if it's a spy first. Why is that so hard to imagine? I could write it as an external utility but that would be coupled to your internal state management which I try not to do.
|
The core problem here is that you seem to assume that I'm spying on something, but I'm actually making a pure mock from spies so that nothing I'm not providing can be called. So rather than mutate dependencies I have no control of (which is how your sandbox and restore work) I never touch the dependencies at all. I'm strictly verifying the parts of their interfaces I use. The dependencies can break and my tests don't, this isolates the cause of failures and makes my unit tests actual unit tests. It also allows me to model specific behaviors in external libraries without a lot of setup. |
@AdamMcCormick the clobbering I was referring to was that the old spy I'm happy to look at accepting a PR with the code you've outlined. I encourage you (or anyone else) to raise a PR with tests & documentation. I'm not sure about the |
Spy should remain immutable. Instead of
reset
ing it, it's better to create a new one