Как использовать rxjs мраморное тестирование с графическим контроллером Apollo Testing в Angular
Я хотел бы проверить подписку GraphQL в Angular, что
- Сделать запрос
- Присоедините подписку к запросу с помощью
subscribeToMore
- Смоделируйте результат запроса, используя команду flush, и подтвердите результат.
- Смоделируйте результат подписки, используя команду 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 тестирование шариков?