3

I would like to test a factory method that runs $resource. My test would mock the real backend that does not exist yet.

Here is a sample factory code:

app.factory( 'Global', function( $resource ){
  var Jogas = $resource('/jogasok/:id', {'id': '@id'}, {
    'ujBerlet': {'method': 'POST', 'params': {'berlet': true}}
  });
  var jogasok = Jogas.query();
  return {
    getJogasok: function() {
      return jogasok;
    }
  };
})

My test would be to check that query was made.

If I initialize my app with:

app.run( function run ($httpBackend) {
  $httpBackend.whenGET('/jogasok').respond([
        {id: 1, name: 'asdfasdf'}, 
        {id: 2, name: '2wrerwert'}
      ]);
})

and open the app in my browser, then everything seems to be fine, I have the dummy data in the browser.

But, when I take out the run code above, and write a test, things just don't work.

describe( 'Global Service: ', function() {

  beforeEach( module('bkJoga') );
  beforeEach( inject(function($httpBackend) {
    $httpBackend.whenGET('/jogasok').respond([
      {id: 1, name: 'asdfasdf'}, 
      {id: 2, name: '2wrerwert'}
    ]);
  }));

  it('getJogasok should return everyone', inject(function(Global) {
    expect(JSON.stringify(Global.getJogasok())).toBe(JSON.stringify([
      {id: 1, name: 'asdfasdf'}, 
      {id: 2, name: '2wrerwert'}
    ]));
  }));
});

fails.

4

5 回答 5

5

这是您发布的工厂的工作测试。我为注入的 httpBackend 添加了一个变量 $httpBackend。并调用 $httpBackend.flush()。fiddle-demo(阅读到最后以获取小提琴内容的完整描述)

describe( 'Global Service: ', function() {
    var Global, $httpBackend

    // load the relevant application module, with the service to be tested
    beforeEach( module('bkJoga') );

    beforeEach( function() {

        // inject the mock for the http backend
        inject(function(_$httpBackend_) {
            $httpBackend = _$httpBackend_;
        });
        // mock the response to a particular get request
        $httpBackend.whenGET('/jogasok').respond([
            {id: 1, name: 'asdfasdf'}, 
            {id: 2, name: '2wrerwert'}
        ]);
        // inject the service to be tested
        inject(function(_Global_) {
            Global = _Global_;
        });
    });

    it('should exist', function() {
        expect(!!Global).toBe(true);
    });

    it('getJogasok should return everyone', function() {
        $httpBackend.flush();    // <------------ need to flush $httpBackend
        expect(JSON.stringify(Global.getJogasok())).toBe(JSON.stringify([
            {id: 1, name: 'asdfasdf'}, 
            {id: 2, name: '2wrerwert'}
        ]));
    });
});

无论如何,我会以不同的方式重写工厂,因为按照当前的编写方式,它仅在实例化时才查询数据库var jogasok = Jogas.query();。因为angularjs 中的服务是单例的,所以在您的应用程序中,您将只拥有实例化时的数据。因此,以后对数据的修改将不会反映在您的工厂中。这是一个反映这个想法的工厂及其单元测试的例子。

工厂:

app.factory('GlobalBest', function ($resource) {
    return $resource('/jogasok/:id', {
        'id': '@id'
    }, {
        'ujBerlet': {
            'method': 'POST',
                'params': {
                'berlet': true
            }
       }
    });
});

考试:

describe('GlobalBest Service: ', function () {
    var srv, $httpBackend;

    beforeEach(module('bkJoga'));

    beforeEach(function () {
        // inject the mock for the http backend
        inject(function (_$httpBackend_) {
            $httpBackend = _$httpBackend_;
        });

        // inject the service to be tested
        inject(function (_GlobalBest_) {
            srv = _GlobalBest_;
        });
    });

    it('should exist', function () {
        expect( !! srv).toBe(true);
    });

    it('query() should return everyone', function () {

        // mock the response to a particular get request
        $httpBackend.whenGET('/jogasok').respond([{
            id: 1,
            name: 'asdfasdf'
        }, {
            id: 2,
            name: '2wrerwert'
        }]);

        // send request to get everyone
        var data = srv.query();

        // flush the pending request
        $httpBackend.flush();
        expect(JSON.stringify(data)).toBe(JSON.stringify([{
            id: 1,
            name: 'asdfasdf'
        }, {
            id: 2,
            name: '2wrerwert'
        }]));
    });

    it('get({id: 1}) should return object with id=1', function () {
        // mock the response to a particular get request
        $httpBackend.whenGET('/jogasok/1').respond({
            id: 1,
            name: 'asdfasdf'
        });
        var datum = srv.get({
            id: 1
        });
        $httpBackend.flush();
        expect(JSON.stringify(datum)).toBe(JSON.stringify({
            id: 1,
            name: 'asdfasdf'
        }));
    });
});

我编写了一个fiddle-demo包含 3 个版本的服务:您的原始服务“Global”,一个返回 query() 方法的新版本“GlobalNew”,最后是一个直接返回 $resource 的版本“GlobalBest”。希望这可以帮助。

于 2013-12-11T20:51:11.903 回答
2

尝试将其从 更改.toBe.toEqual. Jasmine 与 进行对象引用相等toBe,并与toEqual.

于 2013-12-05T22:53:21.107 回答
1

到目前为止,我能得到的最佳答案是创建另一个应用程序,它继承我的应用程序 + 使用 ngMockE2E 服务。这样应该可以模拟出这些请求。

注意:不幸的是,我无法在我的测试环境中使用它

于 2013-12-11T17:22:19.877 回答
0

用于angular.mock.inject创建模拟的服务实例,然后您需要调用flush()mock $httpBackend,这允许测试显式刷新挂起的请求,从而保留后端的异步 api,同时允许测试同步执行。

describe('Global Service: ', function () {
    var srv;

    beforeEach(module('bkJoga'));

    beforeEach(function () {
        angular.mock.inject(function ($injector) {
            srv = $injector.get('Global');
        });
    });

    beforeEach(inject(function ($httpBackend) {
        $httpBackend.flush();
        $httpBackend.whenGET('/jogasok').respond([{
            id: 1,
            name: 'asdfasdf'
        }, {
            id: 2,
            name: '2wrerwert'
        }]);
    }));

    it('getJogasok should return everyone', inject(function (Global) {
        expect(JSON.stringify(Global.getJogasok())).toBe(JSON.stringify([{
            id: 1,
            name: 'asdfasdf'
        }, {
            id: 2,
            name: '2wrerwert'
        }]));
    }));
});

Demo

于 2013-12-11T23:24:08.087 回答
0

一篇关于 AngularJS 测试的好文章也涉及后端模拟:http: //nathanleclaire.com/blog/2013/12/13/how-to-unit-test-controllers-in-angularjs-without-setting-your-hair -着火/

于 2013-12-21T08:30:35.340 回答