You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Karma对Angular中的async ngIf进行单元测试

Testing async + *ngIf in Angular with Karma/Jasmine

Got it, let's walk through how to properly test the *ngIf="results | async as dataResult" logic in your component. I'll break this down into actionable steps with code examples tailored to your template.

1. Setup Your Test Bed & Dependencies

First, you'll need to configure the TestBed to include the modules your component uses, plus mock any services that feed the results Observable. For your code, that means:

  • Importing CommonModule (for *ngIf, *ngFor, and the async pipe)
  • Mocking the TranslateService (since you're using the translate pipe)
  • Providing a mock version of whatever service exposes the results Observable (let's call it SearchService for this example)
import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
import { CommonModule } from '@angular/common';
import { TranslateService } from '@ngx-translate/core';
import { YourComponent } from './your.component';
import { SearchService } from './search.service';
import { of } from 'rxjs';

// Mock TranslateService to avoid real translation calls
class MockTranslateService {
  translate(key: string) {
    // Simulate translation by returning the uppercase key (matches your template logic)
    return of(key.toUpperCase());
  }
}

// Mock the service that provides the `results` Observable
class MockSearchService {
  results$ = of([]); // Default to empty data
}

describe('YourComponent', () => {
  let component: YourComponent;
  let fixture: ComponentFixture<YourComponent>;
  let searchService: MockSearchService;
  let debugElement: DebugElement;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [CommonModule],
      declarations: [YourComponent],
      providers: [
        { provide: TranslateService, useClass: MockTranslateService },
        { provide: SearchService, useClass: MockSearchService }
      ]
    }).compileComponents();

    fixture = TestBed.createComponent(YourComponent);
    component = fixture.componentInstance;
    searchService = TestBed.inject(SearchService) as MockSearchService;
    debugElement = fixture.debugElement;
  });

2. Test When Observable Returns Valid Data

We'll simulate the results$ Observable emitting structured data, then verify the template renders correctly:

it('should render results sections when data is available', fakeAsync(() => {
    // Mock sample data matching your component's expected structure
    const mockData = [
      {
        title: 'locations',
        results: [
          { location: { name: 'New York' } },
          { location: { name: 'London' } }
        ]
      },
      {
        title: 'hotels',
        results: [{ location: { name: 'Grand Hotel' } }]
      }
    ];

    // Update the Observable to emit our mock data
    searchService.results$ = of(mockData);
    fixture.detectChanges(); // Trigger initial change detection
    tick(); // Wait for the async pipe to process the Observable
    fixture.detectChanges(); // Refresh the view with new data

    // Verify the outer container renders
    const outerUl = debugElement.nativeElement.querySelector('ul');
    expect(outerUl).toBeTruthy();

    // Check that translated titles appear correctly
    const titleWrappers = debugElement.nativeElement.querySelectorAll('.wrapper');
    expect(titleWrappers[0].textContent).toBe('LOCATIONS');
    expect(titleWrappers[1].textContent).toBe('HOTELS');

    // Verify all list items render with correct names
    const listItems = debugElement.nativeElement.querySelectorAll('.list__item');
    expect(listItems.length).toBe(3);
    expect(listItems[0].textContent.trim()).toBe('New York');
    expect(listItems[2].textContent.trim()).toBe('Grand Hotel');
  }));

3. Test Click Behavior on Result Items

Verify that clicking a list item triggers the clickResult method and closes the results panel:

it('should call clickResult and close results on item click', fakeAsync(() => {
    const mockRow = { location: { name: 'Paris' } };
    const mockData = [
      {
        title: 'locations',
        results: [mockRow]
      }
    ];

    searchService.results$ = of(mockData);
    fixture.detectChanges();
    tick();
    fixture.detectChanges();

    // Spy on the component's clickResult method
    spyOn(component, 'clickResult');
    // Set results to open initially
    component.isResultsOpen = true;
    fixture.detectChanges();

    // Trigger click on the list item
    const listItem = debugElement.nativeElement.querySelector('.list__item');
    listItem.click();

    // Verify the method was called with the correct row
    expect(component.clickResult).toHaveBeenCalledWith(mockRow);
    // Verify results panel is closed
    expect(component.isResultsOpen).toBeFalse();
  }));

4. Test Edge Cases: Empty or No Data

Ensure the template doesn't render unnecessary sections when there's no valid data:

it('should not render results when the Observable returns empty', fakeAsync(() => {
    searchService.results$ = of([]);
    fixture.detectChanges();
    tick();
    fixture.detectChanges();

    const outerUl = debugElement.nativeElement.querySelector('ul');
    expect(outerUl).toBeFalsy();
  }));

  it('should skip sections where inner results are empty', fakeAsync(() => {
    const mockData = [
      {
        title: 'locations',
        results: [] // Empty inner results
      }
    ];
    searchService.results$ = of(mockData);
    fixture.detectChanges();
    tick();
    fixture.detectChanges();

    const searchSection = debugElement.nativeElement.querySelector('.global-search');
    expect(searchSection).toBeFalsy();
  }));
});

Key Tips

  • Use fakeAsync + tick() to handle the async pipe's asynchronous processing (alternatively, async/await with fixture.whenStable() works too).
  • Always call fixture.detectChanges() after updating component state or data to trigger view updates.
  • Mock all external services to isolate your component and avoid dependencies on real backend or translation services.

内容的提问来源于stack exchange,提问作者Thorsten Rintelen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:16:55