使用Jasmine/Karma进行Angular 4单元测试,如何修复HTTP POST模拟的问题

9

我想在 Angular 4 TypeScript Jasmine 中对一个服务进行单元测试。

现在,http 发送了一个 post 请求,并返回一个标识符,但是它没有发送任何内容。

我只想有良好的代码覆盖率,但我不太明白如何完全编写这个模拟语句。

以下是我的服务文件中的 HTTP POST 方法:

addSession() {
        let headers = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: headers });

        return this.http.post(this.url, JSON.stringify({}), options)
            .map((response: Response) => response.json());

}

然后是SPEC文件,我不确定要测试什么,我想要假装我从服务HTTP POST中收到了一个数字,响应应该是像000000014这样的东西。

规范

import { TrackerFormService } from './tracker-form.service'
import { Observable } from 'rxjs/Observable'

describe('TrackerFormService', () => {

    let trackerFormService: TrackerFormService,
        mockHttp;

    beforeEach(() => {
        mockHttp = jasmine.createSpyObj('mockHttp', ['get', 'post', 'put']
        )
        trackerFormService = new TrackerFormService(mockHttp);
    });

    describe('addSession', () => {

        it('add session ', () => {
              // how to test,  what to test?    
              // response , is a number?  how to mock/fake this?

        })

    })

})
4个回答

9
为了实现你想要的功能,你需要一个简单的函数来返回与POST通常返回相同的模拟数据。另外,你的测试不应该真正地访问服务器,因此你需要像这样的东西(你可能需要添加其他依赖项):
import { HttpModule } from '@angular/http';
import { TrackerFormService } from './tracker-form.service'
import { Observable } from 'rxjs/Observable'

describe('TrackerFormService', () => {
// Mock the service like this and add all the functions you have in this fashion
let trackerFormService: TrackerFormService,
  mockService = {
    addSession: jasmine.createSpy('addSession').and.returnValue(Observable.of('your session object mock goes here'))
  };

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpModule],
      providers: [{
        provide: TrackerFormService,
        useValue: mockService
      }]
    });
  });

  // Do this trick to inject the service every time, and just use `service` in your tests
  beforeEach(inject([TrackerFormService], (trackerFormService) => {
    service = trackerFormService;
  }));

  describe('addSession', () => {
    it('add session ', () => {
      let fakeResponse = null;

      // Call the service function and subscribe to it to catch the fake response coming from the mock.
      service.addSession().subscribe((value) => {
        // in here value will be whatever you put as returnValue (remember to keep the observable.of())
        fakeResponse = value;
      });

      // expects as in any test.
      expect(fakeResponse).toBeDefined();
      expect(fakeResponse).toBe('your session object mock goes here');
    });
  });
});

0

好的,根据你设置测试/模拟的方式,你可以伪造post调用的返回值并检查你得到了预期的结果。这样做可以测试模拟响应是否被你的映射语句正确转换。通过使用spy,你还可以检查post方法的调用方式。这将检查选项是否与你的预期相匹配。

但在我看来,这是一个相当复杂的解决方案。我更喜欢通过将方法拆分成每个方法只做一件事情来避免模拟和spy。因为你的addSession方法目前正在执行三个不同(但逻辑上相关)的任务:

  1. 创建addSession xhr调用的选项
  2. 执行调用
  3. 转换响应

如果你将该方法拆分成三个部分,你可以轻松地在单独的测试中测试方法#1和#3,并且方法#2只包含对http库的调用。这使你可以实现与上述相同的测试价值,而无需调用http库。

现在关于方法#2...它仍然没有经过测试,在我看来根本没有理由进行测试。因为你没有编写那段代码。此外,如果你正在使用angular的http模块,我相信他们自己有可靠的单元测试。

您的服务响应已经被额外的集成测试覆盖,较少地运行检查服务API仍将返回您期望的结果。

如果您确实希望在代码覆盖率中获得一条绿线,那么您可以选择使用一个名为nock的库。 Nock将拦截您的应用程序将引起的所有xhr流量。在测试文件中,您可以使用nock对象将xhr请求映射到模拟响应。

var scope = nock('http://myapp.iriscouch.com')
                .post('/users', {
                  username: 'pgte',
                  email: 'pedro.teixeira@gmail.com'
                })
                .reply(201, {
                  ok: true,
                  id: '123ABC',
                  rev: '946B7D1C'
                });

复制自:https://www.npmjs.com/package/nock

关于测试的参考和额外信息,以及测试的数量建议观看Justin Searls的“预算现实”视频。


0

随着Angular 4.3的到来,HttpClient服务取代了Http并提供了一种更容易模拟HTTP请求的方法。官方页面上有很好的文档:https://angular.io/guide/http


我已经在使用另一个,我想知道如何具体模拟我所拥有的。你所说的相当于1. 在.NET Core或C# 7中,现在可以做XYZ,非常好。或者2.在ES6中,现在更好了... - user6321478
如果我理解正确的话,您想在TrackerFormService的测试中模拟TrackerFormService.addSession()方法,但从我的角度来看这没有意义,因为您想要测试真正的实现而不是模拟。 - TekTimmy
它从响应中返回一个像000000015这样的数字,所以我确信人们已经模拟/伪造了HTTP POST服务的过程。我可以找到一些例子,但它们都非常具体,尝试弄清楚如何仅模拟/伪造我的特定测试是我想要的。我不敢说我知道如何做,所以我在问这个问题。 - user6321478

-2

HTTP服务请求的示例测试用例

describe('Forgot Password Controller', function () {
    var $controller,
        $httpBackend,
        $q,
        $rootScope,
        $state,
        controller,
        scope,
        accountProvider;

    beforeEach(module('app'));
    beforeEach(inject(function (_$injector_, _$controller_, _$rootScope_) {

        $controller = _$controller_;
        $rootScope = _$rootScope_;
        $httpBackend = _$injector_.get('$httpBackend');
        $state = _$injector_.get('$state');
        $q = _$injector_.get('$q');
        accountProvider = _$injector_.get('accountProvider');
        scope = _$rootScope_.$new();

        controller = $controller(app.controllers.forgotPassword, {
            $state: $state,
            accountProvider: accountProvider
        });
    }));

    afterEach(function () {
        $httpBackend.verifyNoOutstandingRequest();
        $httpBackend.verifyNoOutstandingExpectation();
    });

    describe('forgot password submission', function () {

        it('Can submit a forgot password request successfully', function () {
            $httpBackend.expectPOST("URL DOMAIN" + '/events/requestPasswordReset').respond(200);
            spyOn($state, 'go');
            controller.form = { emailAddress: 'aks@gmail.com' };

            controller.submit();

            expect(controller.submitting).toBe(true);

            $httpBackend.flush();

            expect(controller.submitting).toBe(false);
            expect($state.go).toHaveBeenCalledWith('login', { successMessage: 'An email sent to ' + controller.form.emailAddress + ' contains instructions for resetting your password.' });
        });

        it('Can handle when a user is not found when submitting a forgot password request', function () {
            $httpBackend.expectPOST(app.env.EDGE_SERVICE_PATH + '/events/requestPasswordReset').respond(404);
            spyOn($state, 'go');
            controller.form = { emailAddress: 'aks@gmail.com' };

            controller.submit();

            expect(controller.submitting).toBe(true);
            $httpBackend.flush();

            // We intentionally want to make it appear to the user that the password reset email was sent even when a user
            // does not exist, to help hide info about which users exist in the system
            expect(controller.submitting).toBe(false);
            expect($state.go).toHaveBeenCalledWith('login', { successMessage: 'An email sent to ' + controller.form.emailAddress + ' contains instructions for resetting your password.' });

        });

        it('Can handle unexpected errors from submitting a forgot password request', function () {
            $httpBackend.expectPOST("URL DOMAIN"  + '/events/requestPasswordReset').respond(500);

            controller.submit();
            $httpBackend.flush();

            expect(controller.errors.unexpectedError).toBe(true);
        });

        it('Can handle 422 validation errors from submitting a forgot password request', function () {
            var responseData = {
                fieldErrors: {
                    username: [{code: 'error'}, {code: 'required', message: 'This is required.'}]
                }
            };
            $httpBackend.expectPOST("URL DOMAIN" + '/events/requestPasswordReset').respond(422, responseData);

            controller.submit();
            $httpBackend.flush();

            expect(controller.errors.validationErrors).toBe(true);
            expect(controller.errors.fieldErrors).toEqual(responseData.fieldErrors);
        });

        it('Can handle 503 service unavailable from submitting a forgot password request', function () {
            $httpBackend.expectPOST("URL DOMAIN" + '/events/requestPasswordReset').respond(503);

            controller.submit();
            $httpBackend.flush();

            expect(controller.errors.serviceUnavailable).toBe(true);
        });

    });

});


8
这不是一个 Angular 4 的解决方案。 - The Head Rush
是的,我在这里提供有关如何编写 REST API 测试用例的想法。 - AnilKumar
这将如何帮助在ng中模拟服务? - Nizar B.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接