Как использовать rxjs мраморное тестирование с графическим контроллером Apollo Testing в Angular

Я хотел бы проверить подписку GraphQL в Angular, что

  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();
  });
});

Но, как вы можете видеть, проверка результатов в 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 в то же время и мне нужно:

  • ПЕРВЫЙ подписаться на запрос
  • ТОГДА получите объект бэкэнда операций, чтобы иметь возможность сбрасывать данные
  • ТО подтвердите результаты

Можно ли совершить действие между subscribe и validation используя rxjs тестирование шариков?

0 ответов

Другие вопросы по тегам