sinon icon indicating copy to clipboard operation
sinon copied to clipboard

Difficult to "spy on everything except one" with fakes

Open akdor1154 opened this issue 6 years ago • 6 comments

Describe the bug Let's say I want to spy on all properties of an object using fakes. I might do something like:

  const properties = Object.getOwnPropertyNames(o);
  const prototypeProperties = Object.getOwnPropertyNames(
    Object.getPrototypeOf(o)
  );
  for (const k of properties.concat(prototypeProperties)) {
    const prop = o[k];
    if (typeof prop === 'function') {
      const fake = sinon.fake(prop);
      sandbox.replace(o, k, fake);
    }
  }

I might later need to override the behaviour of a particular method, but now I can't:

const fake = sinon.fake.rejects('force an error');
sandbox.replace(o, 'method', fake) // TypeError: Attempted to replace method which is already replaced

Expected behavior Replacing the existing method should be possible. I believe the above is a valid counterexample to the contention of 'replacing an already replaced method is a bug in your test code'.

akdor1154 avatar May 30 '18 02:05 akdor1154

Why wouldn't you just change the behavior instead of replacing with a new one?

Based on your example above: o.method.rejects('force an error') instead of making an entirely new fake.

Please feel free to reopen this issue if this doesn't solve the problem.

fearphage avatar Jul 06 '18 16:07 fearphage

from the fake docs:

A fake is immutable: once created, the behavior will not change.

am I missing something?

akdor1154 avatar Jul 08 '18 00:07 akdor1154

That's my mistake. I have yet to use fakes myself. I still use stubs and spies directly. Can you just restore that one property before you replace it?

o.method.restore();
const fake = sinon.fake.rejects('force an error');
sandbox.replace(o, 'method', fake);

Honestly I've never restored a part of a sandbox before (instead of all at once), but I'm just tossing out ideas. Alternatively you could use something that is mutable (like spies or stubs) for that replacement instead: Based on your code above:

    if (typeof prop === 'function') {
      // conditionally use a mutable fake
      const fake = k === 'method' ? sinon.stub() : sinon.fake(prop);
      sandbox.replace(o, k, fake);
    }

Based on the documentation and the code, things seem to be working as expected though. This doesn't seem like a bug or anything is broken. It seems like in this specific case it would be easiest to update the way you're mocking the methods. Let me know if we can help any further.

fearphage avatar Jul 08 '18 14:07 fearphage

Can you just restore that one property before you replace it?

Nope, fakes can't restore themselves, you need to do it with the sandbox you used to replace them in the first place. And, as you say, sandboxes can't do a partial restore (because before fakes were added, this wasn't really a requirement because you could just mutate the stub or whatever).

I agree it's not a bug, but it is imo a pretty important requirement to be able to use fakes in real-world usage. I don't think the suggestion of "don't use fakes" is really adequate. It looks like fakes were written in order to promote creating immutable mocks (good), but the APIs around using them still seem to have a few holes.

akdor1154 avatar Jul 09 '18 00:07 akdor1154

I completely understand. Thanks for your feedback and filing the issue.

@mroderick Check out this fakes feedback.

fearphage avatar Jul 09 '18 03:07 fearphage

Is this still relevant? If so, what is blocking it? Is there anything you can do to help move it forward?

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.

stale[bot] avatar Dec 27 '23 10:12 stale[bot]