Angular中使用Jest测试实现Control Value Accessor的第三方CDK组件时循环结构转JSON错误的排查与解决
Let's break down the issue and fix your test step by step:
What's Causing the Error?
The "Converting circular structure to JSON" error happens because you're incorrectly assigning a FormGroup directly to the component instance (component.cust = new FormGroup(...)). The <customer-file> component implements ControlValueAccessor, so it's designed to bind to a parent form's control via formControlName—not hold its own form group directly. This incorrect setup creates circular references between the form control and the component's internal providers, which Jest can't serialize during testing.
Corrected Test Implementation
We'll use a test host component to mimic how <customer-file> is used in your application—wrapped inside a parent form with a FormGroup that includes the cust control. This properly sets up the ControlValueAccessor binding without circular references.
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormGroup, FormControl, ReactiveFormsModule } from '@angular/forms'; import { Component } from '@angular/core'; import { customerFileComponent } from './customer-file.component'; import { FormModule, ButtonModule, NoopAnimationsModule } from 'your-module-path'; // Adjust imports as needed // Test host component to wrap the customer-file component in a form context @Component({ template: ` <form [formGroup]="parentForm"> <customer-file formControlName="cust"> <input type="file" (change)="onFileChange($event)" /> </customer-file> </form> ` }) class TestHostComponent { parentForm = new FormGroup({ cust: new FormControl(null) }); onFileChange(event: Event) { // Dummy handler for the input's change event } } describe('customerFileComponent', () => { let hostFixture: ComponentFixture<TestHostComponent>; let hostComponent: TestHostComponent; let customerFileComponent: customerFileComponent; beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [TestHostComponent, customerFileComponent], imports: [ReactiveFormsModule, FormModule, ButtonModule, NoopAnimationsModule], }).compileComponents(); }); beforeEach(() => { hostFixture = TestBed.createComponent(TestHostComponent); hostComponent = hostFixture.componentInstance; // Get the customer-file component instance from the host fixture customerFileComponent = hostFixture.debugElement.children[0].componentInstance; hostFixture.detectChanges(); }); it('should create', () => { expect(customerFileComponent).toBeTruthy(); }); // Example test to verify ControlValueAccessor functionality it('should update form control value when file is selected', () => { // Simulate a file selection (adjust based on your component's implementation) const testFile = new File(['test content'], 'test.txt', { type: 'text/plain' }); const inputElement = hostFixture.nativeElement.querySelector('input[type="file"]'); // Trigger file change event (Jest requires some setup for file inputs) Object.defineProperty(inputElement, 'files', { value: [testFile] }); inputElement.dispatchEvent(new Event('change')); hostFixture.detectChanges(); // Verify the form control has the expected value expect(hostComponent.parentForm.get('cust')?.value).toBeDefined(); // Adjust the assertion based on how your component formats the file value }); });
Key Fixes Explained:
- Test Host Component: We created a wrapper component that uses
<customer-file>exactly as it would be in your app—inside a form with aFormGroupandformControlName. This eliminates the circular reference issue. - Removed Incorrect FormGroup Assignment: The line
component.cust = new FormGroup(...)is gone, as it was the root cause of the circular structure. - Proper Component Access: We retrieve the
<customer-file>instance from the host fixture, ensuring we test the component in its intended context. - Example Functionality Test: Added a test to verify the component correctly updates the form control when a file is selected (adjust this to match your component's specific behavior).
Additional Tips:
- If you don't want to use a host component, you can directly interact with the
ControlValueAccessorinterface methods (writeValue,registerOnChange, etc.) on the component instance, but the host component approach is more representative of real-world usage. - Ensure all required modules (like
ReactiveFormsModule) are imported in the test bed, asControlValueAccessorrelies on Angular's form infrastructure.
内容的提问来源于stack exchange,提问作者Learn AspNet

