如何在 Angular 中通过 graphQL Apollo 测试控制器使用 rxjs 弹珠测试

JoG*_*JoG 5 rxjs apollo graphql angular rxjs-marbles

我想在 Angular 中测试 GraphQL 订阅

  1. 进行查询
  2. 使用以下命令将订阅附加到查询subscribeToMore
  3. 通过在其操作中使用flush来模拟查询结果并验证结果
  4. 通过在其操作上使用flush来模拟订阅结果并验证结果

我按照有关客户端测试的 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();
  });
});
Run Code Online (Sandbox Code Playgroud)

但正如您所看到的,该部分结果的验证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();
  });
});

Run Code Online (Sandbox Code Playgroud)

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

  • 首先订阅查询
  • 然后获取操作后端对象以便能够刷新数据
  • 然后验证结果

subscribe是否可以在和使用 rxjs 测试弹珠之间进行操作validation