-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
Allow to manually destroy a spy #1281
Comments
expect has
Here you have the simplified use-case from above working with Time to reconsider? |
Why doesn't this exist yet? Use case 1: in a more "global" beforeEach, you set up a spy. Then, in a very specific test, you need to override that spy with another spy. Overriding with andCallThrough does not help. |
@brandonros if you simply want to change the strategy for the spy to execute when called, you can always call @tkrotoff if your primary concern with the global spy is call counts continuously increasing, you should also be able to call This still doesn't sound like an issue that requires actually removing the spy. |
@slackersoft if things keep references to the original, or to the spy, such that actually removing the spy changes how |
@ljharb As mentioned in the other issues, Jasmine will clean up any spies that were created with If you are assigning the output of If you want to check for strict equality of a function that you're spying on for some tests:
We haven't implemented this feature yet, because the use cases for unspy that I have seen have all been solvable with existing Jasmine functionality. Since every feature has an ongoing maintenance cost, if we can solve peoples problems with existing code, we prefer to do that as it let's us solve a greater breadth of problems. |
Everything jasmine can do can be solved with other functionality. (e.g. The point is that |
Let me rephrase this then. The only use cases I've seen for when people want to unspy(thing, 'method');
spyOn(thing, 'method').and.callThrough(); This is actually more complicated to use than the existing functionality, so it doesn't seem like a very good reason to add |
Unless those two lines live in different places, in which case separating them into two calls is much better than conflating two goals into the andCallThrough call. |
My point is that this is obviously a wanted feature, even if it is just for semantics. My pull request uses the feature already in place to clear the spies therefore I believe more time and effort is spent by the jasmine team to come up with reasons not to have this feature than would be spent maintaining it. |
I want to clarify the use case that some kind of "remove spy" function would give me.
Is this better done another way? A nice |
@ChrisBellew based on what you have in that example, you should be able to change the strategy for the inner beforeEach(() => {
foo.bar.and.returnValue(2);
}); |
@slackersoft using your example i get TypeError: Cannot read property 'returnValue' of undefined. On Jasmine 2.6.4 in an angular 4 project. |
That probably means that var foo = { bar: () => {} };
describe('one', () => {
beforeEach(() => {
spyOn(foo, 'bar').and.returnValue(1);
});
it('returns 1', () => {
expect(foo.bar()).toEqual(1);
})
describe('two', () => {
beforeEach(() => {
// Remove first spy here?
foo.bar.and.returnValue(2);
});
it('returns 2', () => {
expect(foo.bar()).toEqual(2);
})
});
}) |
for posterity: I came across this page in a search for how to deal with something I want to both mock in a I got around it by using describe('when we expect $location to mean one thing before execution and another thing after', function() {
beforeEach(function() {
//mock out the first call to location search.
spyOn($location, 'search').and.returnValue({
uuid: 'test-current-node-uuid',
customerId: currentCustomer
});
});
it('should have the appropriate query string parameters set, after execution', function() {
objectUnderTest.foo();
//Verify that the second call came through with different info.
expect($location.search).toHaveBeenCalledWith({
uuid: 'some-other-uuid',
customerId: 'some-other-id'
});
});
}); |
@sc-lewis-notestine I'm glad you found something that works for you. Note that you might want to check |
There is also possible to add support for beforeAll(function () {
var oringSpyOn = spyOn;
spyOn = (obj, fnName) => {
var originalValue = obj[fnName];
var spy = oringSpyOn(obj, fnName);
spy.restore = function () {
obj[fnName] = originalValue;
};
return spy;
};
}); |
If there's a way to do this without resetting the spy I'm happy to do it another way but I'm not sure how. I have some components that will cache state upon initialization. Here's a simplified example: function Parent () {
var cachedState = MyApp.getState();
return Child({
isVisible: [cachedState.isPaidAccount]
});
}); Then I have a test like: describe('Parent', function () {
beforeEach(function () {
spyOn(window, 'Child');
this.args = window.Child.calls.argsFor(0)[0];
});
// ...
it('sets visibility to true if isPaidAccount is true', function () {
// ???
}); That Suggestions for a work around? |
@OscarGodson Maybe something got lost in the simplification process, but from what you've got there it sounds like you really just want a unit test of the If you Hope this helps. Thanks for using Jasmine! |
Guys, what is the deal? The community has been asking about a |
This already had a PR #1289 @slackersoft doesn't seem to want people to restore a spy |
The older github issues asking to restore a spy all centered around wanting to clean up spies installed with Another issue centers around the need to change the strategy for the spy, possibly resetting the call counts as well. Using the existing functionality already built into Jasmine seems like it more accurately expresses the intent. spyOn(foo, 'bar').and.callThrough();
foo.bar.calls.reset();
foo.bar.and.returnValue(3); or with spyOn(foo, 'bar').and.callThrough();
spyOn(foo, 'bar').and.returnValue(3); The original post for this issue seems like it could be solved similarly with similar clarity arguments, leaving function signIn(done) {
foo.bar.calls.reset();
doStuff();
expect(foo.bar).toHaveBeenCalledTimes(1);
}
beforeEach(() => {
spyOn(foo, 'bar').and.callThrough();
}); In this case I might also question the assumption that it is valuable to check the call to Looking back at this conversation, it appears I might not have done as well as I had hoped at giving good examples or explaining why I'm asking these questions and suggesting current functionality to solve user's issues. The intent here was never to say that Jasmine will never have this functionality, but that we need to better understand the use case for it to make sure we come up with the right solution. During this discussion of use cases and solutions is not a good time to send in a pull request for the feature, which is why I referred back to this issue and discussion from @UziTech's pull request and left it open pending this discussion. I'm happy to revisit this feature, and pull request, with the new understanding about the differences between people aesthetic choices in their test suite and desire for compatibility with other mocking frameworks. Hopefully this clears up some of the confusion. Thanks for using Jasmine! |
heh... i just ran into this again so i just saw the response Nov 😂 @slackersoft regarding it being cached, it wouldnt happen because I'd be doing like this (this code just has different function names by copy pasta'd spyOn(Devices, 'is').and.returnValue(false);
this.bt = new BaseToolbar();
expect(this.locationButton.visibility).toEqual([false]);
spyOn(Devices, 'is').and.returnValue(true);
this.bt = new BaseToolbar();
expect(this.locationButton.visibility).toEqual([true]); future instances of I ended up fixing it based on your answer. In case anyone else runs into this that code above changed to this: spyOn(Devices, 'is').and.returnValue(false);
this.bt = new BaseToolbar();
expect(this.locationButton.visibility).toEqual([false]);
Devices.is.and.returnValue(true);
this.bt = new BaseToolbar();
expect(this.locationButton.visibility).toEqual([true]); |
It should be implemented. I don't see a reason not to have this feature. Jest already has it. |
It is implemented but usage is defferent: spyOn(a, "method").and.callFake( behaviorA );
// Now you want behavior B instead of A
// (type casting <> is for typescript)
(<jasmine.Spy>a).method.and.callFake( behaviorB );
// And now you want to restore original behavior:
(<jasmine.Spy>a).method.and.stub();
// Behavior B again
(<jasmine.Spy>a).method.and.callFake( behaviorB ); However I propose to add Spy.reset / remove method so it will be possible to use object without "overhead" (for "some" reasons) |
you are wrong I see in the log that when it gets spied in another it(), it errors out that message |
You can use jasmine-unspy to add a method to remove the spy. |
I have how to unspy console.warn now?? I don't think this is that uncommon need to spy on globals - obviously one can refactor to use service/providers everywhere and not use glboals - but is it really the case? |
@argelj289 can you provide a fully contained example suite that demonstrates the behavior you are describing? From your message, I'm not sure I understand what you're seeing and what you expect to be seeing. |
@Antoniossss Jasmine doesn't currently provide a way to remove a spy during the execution of a single spec. This is because Jasmine will clean up spies in between specs to help ensure you don't get test pollution. If you've created the spy in a |
Most of the time I like that you can't restore a spy because it makes it very clear that you don't have to. In Jest I see tests manually restoring mocks and there's confusion whether they need to do that or not. However, there's an edge case -- |
@nbilyk of course you have to - if you care about the call counts or arguments. |
Following the discussion on Google Groups: https://groups.google.com/forum/#!topic/jasmine-js/0RXvFo707EQ, I think my usecase is valid.
Jasmine does not allow to manually delete/destroy a spy and this is limiting.
signIn, signOut... are bricks (i.e simple functions - not Jasmine tests) that are reused to write complex Jasmine test scenarios like:
Remarks:
<spyOn> : bar has already been spied upon
Spies must be created in a before function or a spec
beforeEach(()=> { spyOn(foo, 'bar').and.callThrough(); })
is not the solution becauseexpect(foo.bar).toHaveBeenCalledTimes(1)
will fail with the 2x signIn call (because.toHaveBeenCalledTimes(2)
)The text was updated successfully, but these errors were encountered: