【问题标题】:Angular Karma - Component undefinedAngular Karma - 组件未定义
【发布时间】:2021-06-06 13:48:59
【问题描述】:

我正在尝试对需要解析器、路由器和激活路由作为依赖项的组件进行单元测试。我尝试使用 RouterTestingModule 并模拟我的解析器以在测试模块中提供它们,但它似乎对组件实例的创建有一些副作用。

这是我的组件的代码:

History.component.ts

import { Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { Subscription } from 'rxjs';
import { Transaction } from '../models/transaction.model';

@Component({
  selector: 'app-history',
  templateUrl: './history.component.html',
  styleUrls: ['./history.component.scss']
})
export class HistoryComponent implements OnInit, OnDestroy {
  history: Transaction[] = [];
  selectedTransaction: Transaction | undefined;
  subscription: Subscription = new Subscription();

  constructor(
    private route: ActivatedRoute,
    private router: Router,
  ) { }

  ngOnInit(): void {
    this.history = this.route.snapshot.data.history;
    const routeSubscription = this.route.params.subscribe((params) => {
      if (params.id) {
        this.setSelectedTransaction(+params.id);
      }
    });
    this.subscription.add(routeSubscription);
  }

  setSelectedTransaction(transactionId: number): void {
    const historyTransaction = this.history.find((transaction) => transaction.id === transactionId);
    this.selectedTransaction = historyTransaction;
  }

  displayTransaction(transaction: Transaction): void {
    this.router.navigate(['transactions', transaction.id]);
  }

  ngOnDestroy(): void {
    this.subscription.unsubscribe();
  }
}

这是当前的单元测试:

History.spec.ts

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ActivatedRoute, Resolve, Routes } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { Observable, of } from 'rxjs';
import { Transaction } from '../models/transaction.model';

import { HistoryComponent } from './history.component';
import { HistoryResolver } from './history.resolver';

const mockRawTransactions = [
  {
    "id":"1",
    "created_at":"2016-01-01T08:30:39-0300",
    "counterparty_name":"Uber",
    "debit":"false",
    "credit":"true",
    "amount":"44.20",
    "currency":"EUR",
    "operation_type":"refund",
    "attachements":[  
      {  
          "url":"https:\/\/fakeimg.pl\/350x200\/?text=Hello"
      }
    ],
  }
];
const mockTransactions = mockRawTransactions.map((transaction) => new Transaction(transaction));

class HistoryMockResolver implements Resolve<Transaction[]> {

  resolve(): Observable<Transaction[]> {
    return of(mockTransactions);
  }
}

describe('HistoryComponent', () => {
  const historyRoutes: Routes = [
    { path: 'transactions', component: HistoryComponent },
    { path: 'transactions/:id', component: HistoryComponent },
  ];
  let component: HistoryComponent;
  let fixture: ComponentFixture<HistoryComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [ HistoryComponent ],
      imports: [
        RouterTestingModule.withRoutes(historyRoutes),
      ],
      providers: [
        {
          provide: ActivatedRoute,
          useValue: {
            snapshot: {
              params: { id: 1 },
            },
          },
        },
        { provide: HistoryResolver, useClass: HistoryMockResolver },
      ]
    })
    .compileComponents();
  });

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

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

  it('should display transactions', () => {
    component.history = mockTransactions;
    expect(component.history).toBeDefined();
    expect(component.history).toHaveSize(mockRawTransactions.length);
  });

  it('should display a single transaction', () => {
    component.history = mockTransactions;
    component.displayTransaction(component.history[0]);
    expect(component.selectedTransaction).toBeDefined();
    expect(component.selectedTransaction).toEqual(component.history[0]);
  });
});

在 Karma 中运行测试时,组件已定义并很好地显示在 console.log 中,但 Karma 会为每个测试用例引发错误并将组件评估为未定义。

这是第一个错误:

TypeError: Cannot read property 'history' of undefined
at HistoryComponent.ngOnInit (http://localhost:9876/_karma_webpack_/webpack:/src/app/history/history.component.ts:22:45)
at callHook (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:2486:1)
at callHooks (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:2457:1)
at executeInitAndCheckHooks (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:2408:1)
at refreshView (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:9207:1)
at renderComponentOrTemplate (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:9306:1)
at tickRootContext (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:10532:1)
at detectChangesInRootView (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:10557:1)
at RootViewRef.detectChanges (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:22569:1)
at ComponentFixture._tick (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/__ivy_ngcc__/fesm2015/testing.js:141:1)

我应该在我的测试模块中修复什么?

【问题讨论】:

  • 你能分享一下 karma 打印的错误信息吗?
  • @deepakchethan 我添加了 Karma 触发的第一个错误,以下是基于相同的问题
  • 这里有错误的字段snapshot: {params: { id: 1 }, },。应该是snapshot: {data: { id: 1 }, },。根据通过this.route.snapshot.data.history引用它

标签: javascript angular karma-runner


【解决方案1】:

这是因为 this.route.snapshot.data.history 和数据未定义,因为您没有在模拟激活的快照中传递它。

您可以在History.spec.ts 中更新您的提供程序以获取激活的路线快照

{
  provide: ActivatedRoute,
  useValue: {
    snapshot: {
      params: { id: 1 },
      data: {history: 'something-history-obj'}
    },
  },
},

或者你可以在History.component.ts 中使用this.route.snapshot.data?.history,如果它确实可以为空

【讨论】:

    猜你喜欢
    • 2015-02-10
    • 1970-01-01
    • 1970-01-01
    • 2023-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-24
    相关资源
    最近更新 更多