Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

unit testing angular component with input.required

When my components are like this

export OldComponent {
    @input myInput!: string;
}

I can prepare the unit tests like this:

fixture = TestBed.createComponent(OldComponent);
component = fixture.componentInstance;
component.myInput = 'test';
fixture.detectChanges();

Now that we have input signals (Angular 17)

export NewComponent {
    myInput = input.required<string>();
}

How do we prepare the component for testing? This doesn't compille because an input signal is read-only:

fixture = TestBed.createComponent(NewComponent);
component = fixture.componentInstance;
component.myInput.set('test'); // Does not compile
fixture.detectChanges();

I tried not assigning it for the tests, but obviously it throws: Error: NG0950: Input is required but no value is available yet. Find more at https://angular.io/errors/NG0950

I would not like to use the workaround of using a normal, not required input: input<string>('default value')

like image 338
Ricardo Aranguren Avatar asked Sep 02 '25 16:09

Ricardo Aranguren


2 Answers

To set inputs, you can use ComponentRef<T>.setInput method, which also works with signals:

fixture = TestBed.createComponent(NewComponent);
component = fixture.componentInstance;
fixture.componentRef.setInput('signal-input-name', 'value'); 
// e.g. setInput('myInput', 'test');
fixture.detectChanges();

This method takes the input name as a first parameter and the value to set signal input to as second parameter.

More in angular.dev documentation


EDIT: New API added in Angular 20.1 (commit 2e0c98bd3f)

In Angular 20.1 new API was added to testing package - inputBinding function (and the same for outputs), that allows you to bind component inputs (and outputs) to specified value or other signal.

With static value:

const fixture = TestBed.createComponent(NewComponent, {
  bindings: [
    inputBinding('signal-input-name', () => 'value')
  ]
});
fixture.detectChanges();

With reactive value:

const testedComponentInput = signal('value');
const fixture = TestBed.createComponent(NewComponent, {
  bindings: [
    inputBinding('signal-input-name', testedComponentInput)
  ]
});
// some tests...
testedComponentInput.set('new value!');
// other tests after changing value

As mentioned, new API has more methods, these are: outputBinding and twoWayBinding which allow you to set signals that will be bound to output or will handle two-way binding

like image 59
Adrian Kokot Avatar answered Sep 05 '25 05:09

Adrian Kokot


You can use signalSetFn

import { SIGNAL, signalSetFn } from '@angular/core/primitives/signals';


fixture = TestBed.createComponent(SomeComponent);
component = fixture.componentInstance;
signalSetFn(component.required[SIGNAL], 'test');
fixture.detectChanges();
like image 31
Łukasz Michalak Avatar answered Sep 05 '25 06:09

Łukasz Michalak