4

I'm using Jasmine to write my tests, but I guess I'd have this problem with any other testing framework. Let's say that we have a module foo with two functions, Bar and Baz, which are constructors (but could be just normal functions):

var Bar = exports.Bar = function Bar() {
  this.baz = new Baz();
};

var Baz = exports.Baz = function Baz() {

};

Now I would like to test Bar, but with a fake Baz implementation:

var foo = require('foo');

describe("foo.Bar", function() {
  it("initializes its own Baz", function() {
    spyOn(foo, 'Baz'); // this replaces foo.Baz with a fake implementation
    var bar = new foo.Bar();
    expect(foo.Baz).toHaveBeenCalled();
  });
});

The problem is that this test will fail, because Bar instantiates a new Baz using the variable Baz, which cannot be changed from outside. The only thing that got swapped by using spyOn() is exports.Baz.

The obvious solution is to write this.baz = new exports.Baz(); but it kind of feels awkward. If I have more functions which I want to use inside my module, I would have to always call all of them using the exports. prefix. Is there any other approach here?

Juliusz Gonera
  • 4,658
  • 5
  • 32
  • 35

1 Answers1

1

If you can somehow decouple these two classes, like allow other implementation of the Baz class to be given to bar, then I think that's the best way and you should go for it.


But if you really want to be able to be able to refer to exports.Baz as Baz, then there's one way that I could think of, using with.

It is said that using with is generally a bad practice and should be avoided, I wouldn't use it in my own code, but this is one way to solve it and can even be a legimate use, as long as you know what you are doing.

Here it goes:

with(exports) {

    exports.Bar = function Bar() {
        console.log('this is bar!');
        this.baz = new Baz();
    };

    exports.Baz = function Baz() {
        console.log('original baz!');
    };

}

In another module if you change foo.Baz to something else, Baz inside foo will also look up to it.

I would still recommend finding a way to make these two classes independent of each other and then you can just give Bar whatever implementation of Baz you want.

Community
  • 1
  • 1
Thai
  • 10,746
  • 2
  • 45
  • 57
  • Using `with` is interesting but too hackish I guess. I would prefer not to decouple the two "classes" because `Baz` is never instantiated outside of `Bar`. You could say that maybe I shouldn't test `Baz` then as it's an internal implementation detail but actually the behaviour of the whole system is quite complex and I need more fine-grained tests. Thanks for the tips anyway. – Juliusz Gonera Nov 16 '11 at 20:17