In diesem Beitrag: Angular Form Validierung mit StandardSchemaV1 sind wir bereits über die Bibliothek orval gestolpert, die es uns ermöglicht hat, basierend auf einer openapi.yaml nicht nur unsere Angular-Services und Interfaces zu generieren, sondern auch Validierungs-Regeln für unsere Signal-Formulare. Doch das ist nicht alles. Orval bietet noch weitere Möglichkeiten einen praktischen Nutzen aus der OpenApi zu ziehen, z.B. Mock-Daten.
Zugrunde liegt hier die Bibliothek faker.js die wir im ersten Schritt als dev-Dependency in unser Projekt einbinden
npm install --save-dev @faker-js/faker
Orval wird nun wie bereits im Beitrag oben zu sehen, mit einer entsprechenden Konfiguration versehen, in der wir zusätzlich nun konfigurieren, dass auch die faker-Artefakte (via mock>generators) erstellt werden:
import {defineConfig} from 'orval';
export default defineConfig({
dummyjson: {
output: {
mode: 'tags-split',
target: 'src/app/generated',
schemas: 'src/app/generated',
mock: {
generators: [{
type: 'faker',
}],
},
client: 'angular',
baseUrl: 'https://dummyjson.com',
},
input: {
target: './openapi.yaml',
},
},
});
Damit wird nun z.b. aus einer getUserById Operation die einen Benutzer liefert (id, firstName, lastName, email) ein entsprechendes Mock Objekt erzeugt, das dynamisch mit Zufalls-generierten (gültigen) Werten befüllt wird:
// generated
export const getGetUserByIdResponseMock = (overrideResponse: Partial<Extract<User, object>> = {}): User => ({
id: faker.number.int({
min: 1,
max: 2147483647
}),
firstName: faker.helpers.fromRegExp("[A-Za-zÀ-ÿ' -]+"),
lastName: faker.string.alpha({length: {min: 5, max: 100}}),
email: faker.internet.email(), ...overrideResponse
})
Anmerkung: faker.js unterstützt nicht alle Kombinationen von Validierung-Regeln, insbesondere in Verbindung mit Regex. Zum Beispiel wird bei der Angabe einer Regex in Kombination mit einer Mindestlänge aktuell ein String erzeugt, der zwar der Regex entspricht, aber nicht die korrekte Länge aufweist. Hier müssen ggf. Werte bei konkreten Unit-Tests überschrieben werden (s.u)
Der Test einer Angular Komponente könnte nun so aussehen:
Komponente:
@Component({
...
})
export class SignalValidated {
id = input<number>(1);
defaultService = inject(DefaultService);
private userResource = resource({
params: () => ({id: this.id()}),
loader: ({params}) => firstValueFrom(this.defaultService.getUserById(params.id))
})
Test:
describe('SignalValidated', () => {
let component: SignalValidated;
let fixture: ComponentFixture<SignalValidated>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [SignalValidated],
providers: [
{
provide: DefaultService, useValue: vi.mockObject({
getUserById: vi.fn().mockReturnValue(of(getGetUserByIdResponseMock()))
})
}
]
}).compileComponents();
fixture = TestBed.createComponent(SignalValidated);
component = fixture.componentInstance;
await fixture.whenStable();
});
it('should init with valid form', () => {
expect(Object.values(component.formState).every(e => !!e)).toBeTruthy();
expect(component.form().valid()).toBeTruthy();
})
});
Bei jedem Testdurchlauf, werden wie bereits erwähnt, zufällige Werte generiert. Sollten wir für Testfälle dedizierte Werte benötigen, lässt sich dies durch einen Override des entsprechenden Attributes realisieren:
...
providers: [
{
provide: DefaultService, useValue: vi.mockObject({
getUserById: vi.fn().mockReturnValue(
of(getGetUserByIdResponseMock({firstName: 'Maximilian'}))
)
})
}
]
...
it('should load user on init', () => {
expect(component).toBeTruthy();
expect(serviceMock.getUserById).toHaveBeenCalledWith(1);
expect(component.formState().firstName).toEqual('Maximilian')
});
});
Sicherlich sind solche zufällig generierten Daten nicht für jeden Test geeignet, können aber einfach da zum Zug kommen, wo wir keine konkreten Werte für unsere Tests benötigen, sondern „einfach nur irgendwelche“ Daten. Danke OpenApi. Danke Oval.