Angular ngx-translate Karma test fails with TypeError: this.missingTranslationHandler.handle is not a function

Angular ngx-translate Karma test fails with TypeError: this.missingTranslationHandler.handle is not a function

通过学习本教程 (https://indepth.dev/implementing-multi-language-angular-applications-rendered-on-server/),我已经建立了一个运行良好的简单语言翻译系统。但是当我启动我的测试时我有很多错误然后我将我的 angular 从 8 升级到 10 解决了一些错误但是这个仍然是 remaning 而且我现在不知道如何修复它:TypeError: this.missingTranslationHandler.handle is not a function

有时我也有一个 this.currentLoader.getTranslation is not a function 错误现在不知道为什么。

package.json

"dependencies": {
    "@angular/animations": "~10.0.7",
    "@angular/common": "~10.0.7",
    "@angular/compiler": "~10.0.7",
    "@angular/core": "~10.0.7",
    "@angular/forms": "~10.0.7",
    "@angular/platform-browser": "~10.0.7",
    "@angular/platform-browser-dynamic": "~10.0.7",
    "@angular/router": "~10.0.7",
    "@ngx-translate/core": "^13.0.0",
    "@ngx-translate/http-loader": "^6.0.0",
    "rxjs": "~6.6.2",
    "tslib": "^2.0.0",
    "zone.js": "~0.10.3"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "~0.1000.5",
    "@angular/cli": "~10.0.5",
    "@angular/compiler-cli": "~10.0.7",
    "@angular/language-service": "~10.0.7",
    "@types/node": "^12.11.1",
    "@types/jasmine": "~3.3.8",
    "@types/jasminewd2": "~2.0.3",
    "codelyzer": "^6.0.0",
    "jasmine-core": "~3.5.0",
    "jasmine-spec-reporter": "~5.0.0",
    "karma": "~5.0.0",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage-istanbul-reporter": "~3.0.2",
    "karma-jasmine": "~3.3.0",
    "karma-jasmine-html-reporter": "^1.5.0",
    "protractor": "~7.0.0",
    "ts-node": "~7.0.0",
    "tslint": "~6.1.0",
    "typescript": "~3.9.7"
  }

i18n/i18n.module.ts

@NgModule({
  imports: [
    HttpClientModule,
    TranslateModule.forRoot({
      loader: {
        provide: TranslateLoader,
        useFactory: translateLoaderFactory,
        deps: [HttpClient]
      }
    }),
  ],
  exports: [TranslateModule]
})
export class I18nModule {
  constructor(translate: TranslateService) {
    translate.addLangs(['en', 'fr']);
    const browserLang = translate.getBrowserLang();
    translate.use(browserLang.match(/en|fr/) ? browserLang : 'en');
  }
}

export function translateLoaderFactory(httpClient: HttpClient) {
  return new TranslateHttpLoader(httpClient);
}

app.module.ts

@NgModule({
  declarations: [
    AppComponent,
    NavigationComponent,
    HomeComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    I18nModule,
    TranslateModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

home/home.componenet.ts

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {

  public currentLang: string;
  text = '';

  constructor(private changeDetectorRef: ChangeDetectorRef, public translate: TranslateService) { }

  ngOnInit() {
    this.currentLang = this.translate.currentLang;
    this.translate.onLangChange.subscribe((event: LangChangeEvent) => {
      this.currentLang = event.lang;
      this.changeDetectorRef.detectChanges();
      this.translateText();
    });
    console.log('text: ', this.text);
  }

  translateText() {
    this.translate.get('default').subscribe((data: any) => {
      this.text = data.compA;
      console.log('text: ', this.text);
    });
  }

}

home/home.componenet.html

<select #langSelect (change)="translate.use(langSelect.value)">
    <option *ngFor="let lang of translate.getLangs()" [value]="lang"
        [attr.selected]="lang === translate.currentLang ? '' : null">{{lang}}</option>
</select>

<p>{{'default.compA' | translate}}</p>

<p translate>default.compB</p>

<p>{{text}}</p>

home/home.component.spec.ts

describe('HomeComponent', () => {
  let component: HomeComponent;
  let fixture: ComponentFixture<HomeComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [HomeComponent],
      imports: [
        TranslateModule
      ],
      providers: [
        TranslateService,
        TranslateStore,
        TranslateLoader,
        TranslateCompiler,
        TranslateParser,
        MissingTranslationHandler,
        { provide: USE_DEFAULT_LANG, useValue: undefined },
        { provide: USE_STORE, useValue: undefined },
        { provide: USE_EXTEND, useValue: undefined },
        { provide: DEFAULT_LANGUAGE, useValue: undefined }
      ]
    })
      .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(HomeComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});

assets/i18n/en.json

{
    "default": {
        "compA": "Component A works",
        "compB": "Component B works"
    }
}

assets/i18n/fr.json

{
    "default": {
        "compA": "Le Composant A fonctionne",
        "compB": "Le Composant B fonctionne"
    }
}

如果您首先将 TranslationService 放入提供程序中,或者如果导入中的 TranslationModule 未正确初始化,那么 Karma 会告诉您添加所有这些您不需要的提供程序。

我所做的是直接导入 I18nModule 而不是 TranslationModule 并且只在提供程序中保留 TranslationService

home/home.component.spec.ts

beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [HomeComponent],
      imports: [
        I18nModule
      ],
      providers: [
        TranslateService
      ]
    })
      .compileComponents();
  }));