Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Any way to spy on an entire instance with Jasmine

I have a complex javascript class that has many functions, many of which will throw exceptions if they are called outside of a production environment. I need to pass mock instances of this class to the constructor of another class in my tests, however I do not want any of the complex classes functions to actually be called. What I would like to do is have a fake object that has all the functions and properties of the complex class, but for all the functions to just be jasmine spies that do nothing.

Basically I want to be able to do

var fakeComplexClass = createFakeObject(ComplexClass);

var testInstanceOfSimpleClass = new SimpleClass( fakeComplexClass);

And be sure that if testInstanceOfSimpleClass calls any of the fakeComplexClass functions they will be spies and thus not crash my tests.

I could do something like

var fakeComplexClass = { function1() {};, function2() {}; ... } 

but there are many functions and I have a few different complex classes I need to test so just an easy way to basically spy on every single function in a class is what I need.\

Jasmine does have createSpyObj but it requires that you pass it an array of functions. I don't want to have to maintain that array in my tests if I happen to add or delete functions from the complex class so I'd like something that can just spy on every function that is there.

Thanks in advance.

like image 575
asutherland Avatar asked Nov 14 '22 11:11

asutherland


1 Answers

I created a small lib, which works with jasmine-node.

usage:

    var MyClass = function (param) {
        this.initialize(param);
    };
    MyClass.prototype.initialize = function (param) {
        if (param != "expectedParam")
            throw new TypeError();
    };

    var mock1 = jasmine.createStub(MyClass, ["*"]);
    expect(mock1.constructor).toHaveBeenCalled();
    expect(mock1.initialize).not.toHaveBeenCalled();

    var mock2 = jasmine.createStub(MyClass, ["initialize"], ["expectedParam"]);
    expect(mock2.initialize).toHaveBeenCalled();
    mock2.initialize.andCallThrough();
    expect(mock2.initialize).toThrow(new TypeError());

lib:

jasmine.createStub = function (cls, methods, args) {
    if (!(cls instanceof Function))
        throw new TypeError("Invalid class param.");

    var mockClass = function () {
        this.constructor.apply(this, args || []);
    };

    mockClass.prototype = Object.create(cls.prototype);
    mockClass.prototype.constructor = cls;

    var wrap = function (method) {
        if (!mockClass.prototype[method] instanceof Function)
            throw new TypeError("Cannot mock " + method + " it's not a function.");
        jasmine.getEnv().currentSpec.spyOn(mockClass.prototype, method);
    };

    if (methods) {
        if (!(methods instanceof Array))
            methods = [methods];
        if (methods.length == 1 && methods[0] == "*")
            for (var property in mockClass.prototype) {
                if (mockClass.prototype[property] instanceof Function)
                    wrap(property);
            }
        else
            for (var i = 0; i < methods.length; ++i) {
                var method = methods[i];
                wrap(method);
            }
    }

    return new mockClass();
};

I think this row wont work with jasmine in browser, but did not test it:

jasmine.getEnv().currentSpec.spyOn(mockClass.prototype, method);

Somehow my jasmine-node does not have a jasmine.spyOn method...

like image 69
inf3rno Avatar answered Nov 16 '22 04:11

inf3rno