【问题标题】:graphql / Apollo test case fails Error: Expected one matching operation for criteria "Match DocumentNode", found nonegraphql / Apollo 测试用例失败错误:预期标准“匹配 DocumentNode”的一个匹配操作,找不到
【发布时间】:2022-01-04 12:55:57
【问题描述】:

我正在尝试为我使用 graphql 和 Apollo 的角度服务编写测试。

我收到这些错误:Error: Expected one matching operation for criteria "Match DocumentNode", found none.

Error: Expected no open operations, found 1:

fund-summary.service.spec.ts

    import { TestBed } from '@angular/core/testing';
import {
  ApolloTestingController,
  ApolloTestingModule,
} from 'apollo-angular/testing';
import { FundSummaryService } from './fund-summary.service';
import { ConfigurationId, FundId, ShareClassCode } from '@types';
import { ApolloError, gql } from '@apollo/client/core';
import {
  FundDataServiceParams,
  ProductTypeParameter,
} from './graphql-fund-data.service';
import { DocumentNode, GraphQLError } from 'graphql';
import { SummaryDetail } from '@models';
import { MapperFactory } from '@products/utils/mappers/mapper-factory';
import { SiteConfigService } from '@services/site-config.service';
import { createMockSiteConfigService } from '@test/mock-site-config.service';
import { TranslateService } from '@shared/translate/translate.service';
import { HttpClientTestingModule } from '@angular/common/http/testing';

const fragment: DocumentNode = gql`
  fragment SummaryDetails on SummaryDetails {
    elemname
    elemnamestd
    elemvalue
    sectionstd
    section
    sortorder
    asofdate
  }
`;

const summaryQuery: DocumentNode = gql`
  query Summary(
    $fundid: String!
    $shareclasscode: String!
    $countrycode: String!
    $languagecode: String!
  ) {
    Summary(
      fundid: $fundid
      shareclasscode: $shareclasscode
      countrycode: $countrycode
      languagecode: $languagecode
    ) {
      ...SummaryDetails
    }
  }
  ${fragment}
`;

const fundDataServiceParams: FundDataServiceParams = {
  configurationName: ConfigurationId.MF,
  productType: ProductTypeParameter.MUTUAL_FUNDS,
  fundId: '21412' as FundId,
  shareClassCode: 'SINGLCLASS' as ShareClassCode,
};

const expectedRawSummaryDetails: any = {
  Summary: [
    {
      fundid: '21412',
      shclcode: 'SINGLCLASS',
      cntrycode: 'ZA',
      langcode: 'en_GB',
      sectionstd: 'Fund Information',
      section: '',
      elemnamestd: 'FACTOR_CATEGORY',
      elemname: 'ETF Category',
      elemvaluestd: 'Smart Beta',
      elemvalue: 'Smart Beta',
      preelemvaluestd: '',
      preelemvalue: '',
      sortorder: '25',
      datedriven: 'No',
      asofdate: '',
      asofdatestd: '',
    },
  ],
};

const expectedSummaryDetails: SummaryDetail[] = [
  {
    fundId: '21412',
    shareClassCode: 'SINGLCLASS',
    countryCode: 'ZA',
    languageCode: 'en_GB',
    sectionStd: 'Fund Information',
    section: '',
    elementNameStd: 'FACTOR_CATEGORY',
    elementName: 'ETF Category',
    elementValueStd: 'Smart Beta',
    elementValue: 'Smart Beta',
    preelemValueStd: '',
    preelemValue: '',
    sortOrder: '25',
    dateDriven: 'No',
    asOfDate: '',
    asOfDateStd: '',
  },
];

describe('FundSummaryServiceRegisterMethod', () => {
  let controller: ApolloTestingController;
  let fundSummaryService: FundSummaryService;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [ApolloTestingModule, HttpClientTestingModule],
      providers: [
        FundSummaryService,
        TranslateService,
        MapperFactory,
        { provide: SiteConfigService, useFactory: createMockSiteConfigService },
      ],
    });

    controller = TestBed.inject(ApolloTestingController);
    fundSummaryService = TestBed.inject(FundSummaryService);
  });

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

  fit('should return summary details', (done: DoneFn) => {
    fundSummaryService
      .register(fragment, fundDataServiceParams)
      .subscribe((mappedSummaryDetails) => {
        expect(mappedSummaryDetails).toEqual(expectedSummaryDetails);
        done();
      });

    const op = controller.expectOne(summaryQuery);
    expect(op.operation.variables.fundid).toEqual(fundDataServiceParams.fundId); // same fund is resulted.
    op.flushData(expectedRawSummaryDetails);
  });

});

fund-summary.service.ts

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { Logger } from '@utils/logger';
import { Apollo } from 'apollo-angular';
import { DocumentNode } from 'graphql';
import {
  FundDataServiceParams,
  GraphQLFundDataService,
} from './graphql-fund-data.service';
import { map } from 'rxjs/operators';
import { Product, SummaryDetail } from '@models';
import { MapperParams } from '@products/utils/mappers/type.mapper';
import { MapperFactory } from '@products/utils/mappers/mapper-factory';
import { SummaryDetailsMapper } from '@products/utils/mappers/summary-details.type.mapper';
import { SiteConfigService } from '@services/site-config.service';
import { ProductDTO, SummaryDetailDTO } from '@types';
import { TranslateService } from '@shared/translate/translate.service';
import { ProductMapper } from '@products/utils/mappers/product.type.mapper';

const logger = Logger.getLogger('FundSummaryService');

@Injectable({
  providedIn: 'root',
})
export class FundSummaryService extends GraphQLFundDataService {
  constructor(
    apollo: Apollo,
    siteConfigService: SiteConfigService,
    private translateService: TranslateService,
    private mapperFactory: MapperFactory
  ) {
    super(apollo, siteConfigService);
  }

  public register(
    query: DocumentNode,
    fundDataServiceParams: FundDataServiceParams
  ): Observable<[Product, SummaryDetail[]]> {
    return super.register(query, this.getVariables(fundDataServiceParams)).pipe(
      map((rawSummaryDetails) => {
        logger.debug('Summary details response: ', rawSummaryDetails);

        const overviewDto: ProductDTO = rawSummaryDetails?.data?.Overview
          ? rawSummaryDetails?.data?.Overview
          : null;
        const overviewMapperParams: MapperParams = {
          config: this.siteConfigService,
          translateService: this.translateService,
          includeSoftClosed: true,
        };
        const mappedOverview: Product = overviewDto
          ? this.mapperFactory
              .createMapper(ProductMapper, overviewMapperParams)
              .toDomain(overviewDto)
          : null;

        const summaryDetailsDto: SummaryDetailDTO[] = rawSummaryDetails?.data
          ?.Summary
          ? rawSummaryDetails.data.Summary
          : [];
        const summaryDetailsMapperParams: MapperParams = {
          config: this.siteConfigService,
          fundId: fundDataServiceParams.fundId,
          translateService: this.translateService,
          taxonomy: mappedOverview?.productTaxonomy,
        };
        const mappedSummaryDetails: SummaryDetail[] = this.mapperFactory
          .createMapper(SummaryDetailsMapper, summaryDetailsMapperParams)
          .toDomain(summaryDetailsDto);

        return [mappedOverview, mappedSummaryDetails];
      })
    );
  }

  /**
   * Return required variables value to graphql query.
   * @param fundDataServiceParams params required for graphql query
   */
  private getVariables(
    fundDataServiceParams: FundDataServiceParams
  ): Record<string, any> {
    return {
      countrycode: this.getCountry(),
      languagecode: this.getLanguage(),
      fundid: fundDataServiceParams.fundId,
      shareclasscode: fundDataServiceParams.shareClassCode,
    };
  }
}

注意:我试过这个old post's suggestion,没用。

【问题讨论】:

    标签: angular graphql jasmine karma-jasmine apollo


    【解决方案1】:

    对您来说好消息是,在afterEach 中,controller.verify() 失败意味着队列中有一个查询,但并非所有查询都已处理。我认为您的 summaryQueryafterEach 所说的仍在队列中且尚未刷新的查询不完全匹配。

    我认为您可以使用与documentation 不同的expectOne 定义来匹配队列中的查询。我对 Apollo 或 GraphQL 没有太多经验,但可以为 expectOne 尝试类似的方法:

    const op = controller.expectOne(operation => {
      // can assert some stuff on the operation like the documentation shows
      return true;
    });
    

    如果上述方法有效,则很可能意味着summaryQueryApolloTestingController 看到的查询不匹配。

    【讨论】:

      猜你喜欢
      • 2019-06-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-29
      • 2016-04-14
      • 1970-01-01
      相关资源
      最近更新 更多