Skip to content

Default value (object of type Proxy) can give false positives when testing validations #165

Description

@SergiiVolodkoWorking

Hi,
Thanks for an amazing lib! It definitely makes work with JavaScript more human-friendly! :)
I've bumped into a small difference with NSubstitute behaviour and wondering if maybe I just can't find a proper syntax to turn on or off a setting to make things work similarly to what I used to in C#:)

So what I'm trying to achieve.
I have an interface

interface Worker {
   SUPPORTED_WORK: number[];
   doSomething();
}

And this kind of usage of the interface in some service:

constructor (private worker: Worker) {}
....

public someServiceMethod(workId) {
   if (!this.worker.SUPPORTED_WORK.includes(workId)) {
      throw Error("Invalid work");
   }
...

Currently, when I setup a test to cover this validation, if I don't specifying any behaviour for SUPPORTED_WORK property, the test automatically passes.

const worker = Substitute.for<Worker>();
const service = new MyService(worker);

service.someServiceMethod(42);
...//some test logic

Basing on the NSubstitute behaviour, I would expect this usage to throw an error, since the SUPPORTED_WORK property is missing or .include behaviour returns null or something like that by default.

At the moment, if the mock is unset .include(workId) returns a Proxy object, which is truthy. It makes the if-statement pass and giving a false positive in my test.
Even if I wrap this check into some method like:

   if (!this.worker.isWorkSupported(workId)) {
      throw Error("Invalid work");
   }

The issue remains.

Wondering, if I can just tweak some setting to prevent false positives in that kind of checks when I miss to setup returns of my mock?

Activity

  1. notanengineercom commented on Jan 15, 2021

    @notanengineercom
    Collaborator

    Hi @SergiiVolodkoWorking
    Thanks for the detailed issue report!
    Due to javascript limitations, you need to setup the mock to return false (to make your example work).
    If we threw everytime a function or property of a mock is called, we wouldn't be able to record that execution -> no assertions possible (received, didNotReceive, ...).
    And we need to return a proxy in order to handle the mock functions:

    worker.isWorkSupported(Arg.any()).returns(true) // if worker.isWorkSupported() returned null, a TypeError would be thrown

    If you had any ideas how this could change / improve, we are happy to hear suggestions and feedback

  2. notanengineercom commented on Sep 12, 2021

    @notanengineercom
    Collaborator

    The only workaround I can think of, is implementing a custom Symbol.toPrimitive function.
    That should cast the proxy into 0, '' or null(or undefined?).
    What do you think @ffMathy ?

  3. ffMathy commented on Sep 13, 2021

    @ffMathy
    Owner

    Hmm not sure what you mean here. Can you elaborate? 😅🙏

  4. notanengineercom commented on Sep 13, 2021

    @notanengineercom
    Collaborator

    Hmm I think it was late last night and I was sleepy 😪
    I somehow missed that .toPrimitive only works for some of the unary operators

  5. added this to the v2.0 milestone on Mar 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions