5

我想在 Angular 中测试一个 GraphQL 订阅

  1. 进行查询
  2. 使用附加到查询的订阅subscribeToMore
  3. 通过对其操作使用 flush 来模拟查询结果并验证结果
  4. 通过对其操作使用刷新来模拟订阅结果并验证结果

通过遵循有关客户端测试的 Apollo 文档,我成功地进行了良好的测试:

const ENTITY_QUERY = gql`
  query EntityList {
    entityList {
      id
      content
    }
  }
`;

const ENTITY_SUBSCRIPTION = gql`
  subscription OnNameChanged {
    nameChanged {
      id
      name
    }
  }
`;

describe('Test Subscription', () => {
  let backend: ApolloTestingController;
  let apollo: Apollo;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [ApolloTestingModule],
      providers: [
        {
          provide: APOLLO_TESTING_CACHE,
          useValue: new InMemoryCache({ addTypename: true })
        }
      ]
    });
    backend = TestBed.get(ApolloTestingController);
    apollo = TestBed.get(Apollo);
  });

  it('should subscribe and return updated entity', done => {

    const queryRef: QueryRef<any> = apollo.watchQuery({ query: ENTITY_QUERY });

    queryRef.subscribeToMore({
      document: ENTITY_SUBSCRIPTION,
      updateQuery: (entityList, { subscriptionData }) => ({
        entityList: entityList.map(entity => {
          // update name of corresponding entity in cache
          return entity.id === subscriptionData.data.nameChanged.id
            ? {
                ...entity,
                name: subscriptionData.data.nameChanged.name
              }
            : entity;
        })
      })
    });

    const queryResult = [{ id: '1', name: 'John' }, { id: '2', name: 'Andrew' }];
    const subscriptionResult = { id: '1', name: 'Marc' };

    const expectedEntitiesWhenQuery = queryResult;
    const expectedEntitiesAfterSubscriptionUpdate = [subscriptionResult, { id: '2', name: 'Andrew' }];

    let firstQueryTick = true;

    // the subscription should be done before the flush in other case the operation backends would not be available
    queryRef.valueChanges.subscribe(result => {
      try {
        if (firstQueryTick) {

          // first test the query result returns the expected
          expect(result).toEqual(expectedEntitiesWhenQuery);

          firstQueryTick = false;

        } else {

          // then, when the subscription return a new name, test that the result is modified
          expect(result).toEqual(expectedEntitiesAfterSubscriptionUpdate);

          done();
        }
      } catch (error) {
        fail(error);
      }
    });

    // retrieves the query operation backend
    const backendSubscription = backend.expectOne('OnNameChanged');

    // retrieves the subscription operation backend
    const backendQuery = backend.expectOne('EntityList');

    // Mock by flushing data to query
    backendQuery.flush({ data: { entityList: queryResult } });

    // Then Mock by flushing data to subscription
    backendSubscription.flush({ data: { nameChanged: subscriptionResult } });
  });

  afterEach(() => {
    backend.verify();
  });
});

但是正如您所看到的,该subscribe部分结果的验证对于变量来说并不是很干净firstQueryTick......想象一下如果我想测试 10 个结果......

所以我尝试使用rxjs 大理石测试来替换这部分:

import { APOLLO_TESTING_CACHE, ApolloTestingController, ApolloTestingModule } from 'apollo-angular/testing';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { TestBed } from '@angular/core/testing';
import { Apollo, QueryRef } from 'apollo-angular';
import gql from 'graphql-tag';
import { TestScheduler } from 'rxjs/testing';

const ENTITY_QUERY = gql`
  query EntityList {
    entityList {
      id
      content
    }
  }
`;

const ENTITY_SUBSCRIPTION = gql`
  subscription OnNameChanged {
    nameChanged {
      id
      name
    }
  }
`;

describe('Test Subscription', () => {
  let backend: ApolloTestingController;
  let apollo: Apollo;
  let scheduler: TestScheduler;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [ApolloTestingModule],
      providers: [
        {
          provide: APOLLO_TESTING_CACHE,
          useValue: new InMemoryCache({ addTypename: true })
        }
      ]
    });

    backend = TestBed.get(ApolloTestingController);
    apollo = TestBed.get(Apollo);

    scheduler = new TestScheduler((actual, expected) => {
      expect(actual).toEqual(expected);
    });
  });

  it('should subscribe and return updated entity', done => {
    const queryRef: QueryRef<any> = apollo.watchQuery({ query: ENTITY_QUERY });
    queryRef.subscribeToMore({
      document: ENTITY_SUBSCRIPTION,
      updateQuery: (entityList, { subscriptionData }) => ({
        entityList: entityList.map(entity => {
          // update name of corresponding entity in cache
          return entity.id === subscriptionData.data.nameChanged.id
            ? {
                ...entity,
                name: subscriptionData.data.nameChanged.name
              }
            : entity;
        })
      })
    });

    const queryResult = [{ id: '1', name: 'John' }, { id: '2', name: 'Andrew' }];
    const subscriptionResult = { id: '1', name: 'Marc' };


    /////////////////////////////NEW PART

    scheduler.run(({ expectObservable }) => {
      // the source is the query observable
      const source$ = queryRef.valueChanges;

      const expectedMarble = 'x-y|';
      const expectedValues = { x: queryResult, y: [subscriptionResult, { id: '2', name: 'Andrew' }] };

      // this is subscribing and validating at the same time so it is not possible to do something between the subscription and the flush of values from rxjs
      expectObservable(source$).toBe(expectedMarble, expectedValues);
    });

    /////////////////////////////

    // this will not be called because the test is already failing with expectObservable, if we put this part before, it will fail because the subscription is not already done...
    const backendSubscription = backend.expectOne('OnNameChanged');
    const backendQuery = backend.expectOne('EntityList');

    backendQuery.flush({ data: { entityList: queryResult } });
    backendSubscription.flush({ data: { nameChanged: subscriptionResult } });
  });

  afterEach(() => {
    backend.verify();
  });
});

多次尝试后,我无法使其正常工作,因为“同时”expectObservable正在执行subscribe+ validation,我需要:

  • FIRST 订阅查询
  • 然后获取操作后端对象以刷新数据
  • 然后验证结果

是否可以在subscribevalidation使用 rxjs 测试弹珠之间进行操作?

4

0 回答 0