Javascript测试框架Jasmine(五):Spies

Spies是Jasmine里面比较高端的方法,可以用来模拟函数的执行,以达到隔离复杂依赖的效果。例如,你要测试列表的处理是否正确,但是数据是异步请求接口取得的,这时你就可以使用Spies,模拟返回各种不同的数据进行测试。

spy可以保存任何函数的调用记录和输入的参数,spy只能存在于describe和it中,在spec执行完之后销毁。先说两个针对spies的Matcher,toHaveBeenCalled 用来测试函数是否被调用过;toHaveBeenCalledWith 用来测试函数被调用时的参数列表,匹配的话返回true ,看个例子:

describe("A spy", function() {
    var foo, bar = null;

    beforeEach(function() {
        foo = {
            setBar: function(value) {
                bar = value;
            }
        };

        spyOn(foo, 'setBar');

        foo.setBar(123);
        foo.setBar(456, 'another param');
    });

    it("tracks that the spy was called", function() {
        expect(foo.setBar).toHaveBeenCalled();
    });

    it("tracks all the arguments of its calls", function() {
        expect(foo.setBar).toHaveBeenCalledWith(123);
        expect(foo.setBar).toHaveBeenCalledWith(456, 'another param');
    });

    it("stops all execution on a function", function() {
        expect(bar).toBeNull();
    });
});

and.callThrough

spy链式调用and.callThrough 后,在获取spy的同时,调用实际的函数,看示例:

describe("A spy, when configured to call through", function() {
    var foo, bar, fetchedBar;

    beforeEach(function() {
        foo = {
            setBar: function(value) {
                bar = value;
            },
            getBar: function() {
                return bar;
            }
        };

        spyOn(foo, 'getBar').and.callThrough();

        foo.setBar(123);
        fetchedBar = foo.getBar();
    });

    it("tracks that the spy was called", function() {
        expect(foo.getBar).toHaveBeenCalled();
    });

    it("should not effect other functions", function() {
        expect(bar).toEqual(123);
    });

    it("when called returns the requested value", function() {
        expect(fetchedBar).toEqual(123);
    });
});

如果不加and.callThrough 的话,最后一个测试中的fetchedBar ,其实只是spyOn 方法的返回值,并不会执行原始的函数,返回123。

and.returnValue

spy链式调用and.returnValue ,返回指定值。

describe("A spy, when configured to fake a return value", function() {
    var foo, bar, fetchedBar;

    beforeEach(function() {
        foo = {
            setBar: function(value) {
                bar = value;
            },
            getBar: function() {
                return bar;
            }
        };

        spyOn(foo, "getBar").and.returnValue(745);

        foo.setBar(123);
        fetchedBar = foo.getBar();
    });

    it("tracks that the spy was called", function() {
        expect(foo.getBar).toHaveBeenCalled();
    });

    it("should not effect other functions", function() {
        expect(bar).toEqual(123);
    });

    it("when called returns the requested value", function() {
        expect(fetchedBar).toEqual(745);
    });
});

and.callFake

spy链式调用and.callFake ,执行指定函数。

describe("A spy, when configured with an alternate implementation", function() {
    var foo, bar, fetchedBar;

    beforeEach(function() {
        foo = {
            setBar: function(value) {
                bar = value;
            },
            getBar: function() {
                return bar;
            }
        };

        spyOn(foo, "getBar").and.callFake(function() {
            return 1001;
        });

        foo.setBar(123);
        fetchedBar = foo.getBar();
    });

    it("tracks that the spy was called", function() {
        expect(foo.getBar).toHaveBeenCalled();
    });

    it("should not effect other functions", function() {
        expect(bar).toEqual(123);
    });

    it("when called returns the requested value", function() {
        expect(fetchedBar).toEqual(1001);
    });
});

and.throwError

spy链式调用and.throwError ,抛出异常:

describe("A spy, when configured to throw an error", function() {
    var foo, bar;

    beforeEach(function() {
        foo = {
            setBar: function(value) {
                bar = value;
            }
        };

        spyOn(foo, "setBar").and.throwError("quux");
    });

    it("throws the value", function() {
        expect(function() {
            foo.setBar(123)
        }).toThrowError("quux");
    });
});

and.stub

spy恢复到原始状态,不执行任何操作。直接看下源码比较好懂 https://github.com/pivotal/jasmine/blob/master/src/core/SpyStrategy.js 

describe("A spy", function() {
    var foo, bar = null;

    beforeEach(function() {
        foo = {
            setBar: function(value) {
                bar = value;
            }
        };

        spyOn(foo, 'setBar').and.callThrough();
    });

    it("can call through and then stub in the same spec", function() {
        foo.setBar(123);
        expect(bar).toEqual(123);

        foo.setBar.and.stub();
        bar = null;

        foo.setBar(123);
        expect(bar).toBe(null);
    });
});

jasmine.createSpy

当没有函数可以spy的时候,可以创建一个“基础的”spy,它的背后没有任何实际的操作,例如:

describe("A spy, when created manually", function() {
    var whatAmI;

    beforeEach(function() {
        whatAmI = jasmine.createSpy('whatAmI');

        whatAmI("I", "am", "a", "spy");
    });

    it("is named, which helps in error reporting", function() {
        expect(whatAmI.and.identity()).toEqual('whatAmI');
    });

    it("tracks that the spy was called", function() {
        expect(whatAmI).toHaveBeenCalled();
    });

    it("tracks its number of calls", function() {
        expect(whatAmI.calls.count()).toEqual(1);
    });

    it("tracks all the arguments of its calls", function() {
        expect(whatAmI).toHaveBeenCalledWith("I", "am", "a", "spy");
    });

    it("allows access to the most recent call", function() {
        expect(whatAmI.calls.mostRecent().args[0]).toEqual("I");
    });
});

jasmine.createSpyObj

使用jasmine.createSpyObj 可以模拟多个spies。给jasmine.createSpyObj 传入一个字符串数组,返回一个对象,字符串数组的每一项都是它的spy类型的属性。

describe("Multiple spies, when created manually", function() {
    var tape;

    beforeEach(function() {
        tape = jasmine.createSpyObj('tape', ['play', 'pause', 'stop', 'rewind']);

        tape.play();
        tape.pause();
        tape.rewind(0);
    });

    it("creates spies for each requested function", function() {
        expect(tape.play).toBeDefined();
        expect(tape.pause).toBeDefined();
        expect(tape.stop).toBeDefined();
        expect(tape.rewind).toBeDefined();
    });

    it("tracks that the spies were called", function() {
        expect(tape.play).toHaveBeenCalled();
        expect(tape.pause).toHaveBeenCalled();
        expect(tape.rewind).toHaveBeenCalled();
        expect(tape.stop).not.toHaveBeenCalled();
    });

    it("tracks all the arguments of its calls", function() {
        expect(tape.rewind).toHaveBeenCalledWith(0);
    });
});

其他跟踪属性

spy的任何调用都可以使用calls 属性跟踪

.calls.any()

如果spy没有被调用过,则返回false,否则返回true

.calls.count()

返回spy被调用的次数

.calls.argsFor(index)

返回spy被第index次调用时的参数

.calls.allArgs()

返回spy所有被调用的参数

.calls.all()

返回spy所有被调用的this 上下文和参数

.calls.mostRecent()

返回spy最近一次被调用的this 上下文和参数

.calls.first()

返回spy第一次被调用的this 上下文和参数

.calls.reset()

清空spy的所有跟踪,看看他们的用法,点这里看在线运行效果:

describe("Jasmine Spy的跟踪属性", function() {
    var foo, bar = null;

    beforeEach(function() {
        foo = {
            setBar: function(value) {
                bar = value;
            }
        };

        spyOn(foo, 'setBar');
    });

    it(".calls.any()示例", function() {
        expect(foo.setBar.calls.any()).toEqual(false);

        foo.setBar();

        expect(foo.setBar.calls.any()).toEqual(true);
    });

    it(".calls.count()示例", function() {
        expect(foo.setBar.calls.count()).toEqual(0);

        foo.setBar();
        foo.setBar();

        expect(foo.setBar.calls.count()).toEqual(2);
    });

    it(".calls.argsFor(index)示例", function() {
        foo.setBar(123);
        foo.setBar(456, "baz");

        expect(foo.setBar.calls.argsFor(0)).toEqual([123]);
        expect(foo.setBar.calls.argsFor(1)).toEqual([456, "baz"]);
    });

    it(".calls.allArgs()示例", function() {
        foo.setBar(123);
        foo.setBar(456, "baz");

        expect(foo.setBar.calls.allArgs()).toEqual([
            [123],
            [456, "baz"]
        ]);
    });

    it(".calls.all()示例", function() {
        foo.setBar(123);

        expect(foo.setBar.calls.all()).toEqual([{
            object: foo,
            args: [123]
        }]);
    });

    it(".calls.mostRecent()示例", function() {
        foo.setBar(123);
        foo.setBar(456, "baz");

        expect(foo.setBar.calls.mostRecent()).toEqual({
            object: foo,
            args: [456, "baz"]
        });
    });

    it(".calls.first()示例", function() {
        foo.setBar(123);
        foo.setBar(456, "baz");

        expect(foo.setBar.calls.first()).toEqual({
            object: foo,
            args: [123]
        });
    });

    it(".calls.reset()示例", function() {
        foo.setBar(123);
        foo.setBar(456, "baz");

        expect(foo.setBar.calls.any()).toBe(true);

        foo.setBar.calls.reset();

        expect(foo.setBar.calls.any()).toBe(false);
    });
});

下一篇讲一下异步代码测试 –



标签: , ,

无觅相关文章插件,快速提升流量