0

I need to spyOn a method of an object created dynamically inside another method

So consider the following:

public doStuff = () => {
    const myThing = new MyThing();
    myThing.doSomethingElse().then((data) => {
        //do more stuff here...
    })
}

I want to therefore spyOn the instance of MyThing and the call to doSomethingElse().

I did come across a solution here which make use of the object's prototype, which I attempted like so

spyOn(MyThing.prototype, 'doSomethingElse').and.returnValue(Promise.resolve({foo: 'bar'}));

But this does not work after I call doStuff() in my tests, I get an error:

Error: : doSomethingElse() method does not exist

But I know this method is fine, since it runs as expected locally.

I am unsure how to proceed, can anyone assist? Thanks

mindparse
  • 6,115
  • 27
  • 90
  • 191
  • Can you show more of your test? The `imports` and the initialization of variables. Also, can you show the content of `MyThing`? – Castro Roy Jul 11 '19 at 14:27
  • Your main problem lies in a strongly coupled design, doStuff has a hard dependency on MyThing, this is not an easily testable design. Your objects should be loosely coupled with dependencies injected via paramaters. public doStuff = (myThing) and then myThing can be injected at runtime time including tests. – Adrian Brand Jul 11 '19 at 23:33

2 Answers2

0

You might be missing something in your test. This is a simple test and you can see it works as you expected

require("jasmine");

class MyThing {
  async doSomethingElse() {
    return { bar: "foo" };
  }
}

class Stuff {
  doStuff() {
    const myThing = new MyThing();
    myThing.doSomethingElse().then(data => {
      console.log(data);
    });
  }
}

describe("doSomethingElse", () => {
  it("toHaveBeenCalled", () => {
    spyOn(MyThing.prototype, "doSomethingElse").and.returnValue(
      Promise.resolve({ foo: "bar123" })
    );

    const stuff = new Stuff();
    stuff.doStuff();

    expect(MyThing.prototype.doSomethingElse).toHaveBeenCalled();
  });
});

The key is to use the spyOn before you instantiate the class that contains the doStuff function. If we move the const stuff = new Stuff(); above the spyOn it fails.

Hope it helps

Castro Roy
  • 7,623
  • 13
  • 63
  • 97
0

You can create a spy object with mock method by passing in an object where the property names represent returned data for methods.

describe('test', () => {
  let mock;
  beforeEach(() => {
    mock = jasmine.createSpyObj('mock', {
      doSomethingElse: Promise.resolve({foo: 'bar'})
    });
  });
  
  it('call mock', async () => {
    const result = await mock.doSomethingElse();
    expect(result.foo).toEqual('bar');
  });
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.4.0/jasmine.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.4.0/jasmine.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.4.0/jasmine-html.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.4.0/boot.js"></script>
Adrian Brand
  • 20,384
  • 4
  • 39
  • 60