Mocking NgRx ComponentStore? #7792
-
What's the recommended way of mocking a ComponentStore instance? I have the following Component: import { Component, OnInit } from '@angular/core';
import { WorkerJobStatusDetailComponent } from './component/worker-job-status-detail.component';
import { WorkerJobStatusDetailStore } from 'src/app/features/worker-job-status/worker-job-status-detail/store/worker-job-status-detail.store';
import { WorkerJobStatusService } from '../worker-job-status.service';
import { ActivatedRoute } from '@angular/router';
import { Observable, of, switchMap, tap, zip } from 'rxjs';
@Component({
selector: 'app-worker-job-status-detail-container',
standalone: true,
imports: [WorkerJobStatusDetailComponent],
template: '<app-worker-job-status-detail />',
styles: `
:host {
display: block;
width: 100%;
}
`,
providers: [WorkerJobStatusDetailStore],
})
export class WorkerJobStatusDetailContainerComponent implements OnInit {
constructor(
private readonly _workerJobStatusDetailStore: WorkerJobStatusDetailStore,
private readonly _backend: WorkerJobStatusService,
private readonly _route: ActivatedRoute
) {
console.debug('WorkerJobStatusDetailContainerComponent::constructor', {
_workerJobStatusDetailStore,
_backend,
_route,
});
}
ngOnInit(): void {
console.debug('WorkerJobStatusDetailContainerComponent::ngOnInit');
const getWorkerJobStatus$ = (
this._route.params as Observable<{ jobId: string }>
).pipe(
tap((params) => console.debug('Goot params', params)),
switchMap(({ jobId }) =>
zip(of(jobId), this._backend.getWorkerJobStatus(jobId))
)
);
getWorkerJobStatus$.subscribe(([jobId, status]) => {
console.debug('In subscribe', { jobId, status });
this._workerJobStatusDetailStore.setJobId(jobId);
this._workerJobStatusDetailStore.setStatus(status);
});
}
} The store: import { Injectable } from '@angular/core';
import { ComponentStore } from '@ngrx/component-store';
import { WorkerJobStatus } from '../../types';
export interface WorkerJobStatusDetailState {
/**
* A unique ID for this record
*/
jobId: string;
/**
* The status of the given job
*/
status: WorkerJobStatus;
}
const initialState: WorkerJobStatusDetailState = {
jobId: '',
status: {
worker: '',
job: '',
jobStatus: '',
expiration: new Date(),
statusDate: new Date(),
},
};
@Injectable()
export class WorkerJobStatusDetailStore extends ComponentStore<WorkerJobStatusDetailState> {
constructor() {
super(initialState);
}
/*
*** SELECTORS ***
* These produce a subset of the total component state
*/
readonly jobId$ = this.select(({ jobId }) => jobId);
readonly status$ = this.select(({ status }) => status);
readonly setJobId = this.updater(
(state, jobId: string | null): WorkerJobStatusDetailState => ({
...state,
jobId: jobId || '',
})
);
readonly setStatus = this.updater(
(state, status: WorkerJobStatus | null): WorkerJobStatusDetailState => ({
...state,
status: status || initialState.status,
})
);
} And the corresponding test: import { MockBuilder, MockInstance, MockRender } from 'ng-mocks';
import { WorkerJobStatusDetailContainerComponent } from './worker-job-status-detail-container.component';
import { WorkerJobStatusDetailStore } from './store/worker-job-status-detail.store';
import { WorkerJobStatusService } from '../worker-job-status.service';
import { Observable, of } from 'rxjs';
import { WorkerJobStatus } from '../types';
import { ActivatedRoute, Params } from '@angular/router';
import { randomDate } from '@common/testing/util';
const mockGetWorkerJobStatus = jest.fn();
const mockSetJobId = jest.fn();
const mockSetStatus = jest.fn();
let mockParams: Observable<Params> = of({ mock: 'params' });
class mockActivatedRoute {
get params() {
return mockParams;
}
}
describe('WorkerJobStatusDetailContainerComponent', () => {
MockInstance.scope();
beforeEach(() =>
MockBuilder(WorkerJobStatusDetailContainerComponent)
.provide({
provide: WorkerJobStatusService,
useValue: { getWorkerJobStatus: mockGetWorkerJobStatus },
})
.provide({
provide: WorkerJobStatusDetailStore,
useValue: {
setJobId: mockSetJobId,
setStatus: mockSetStatus,
},
})
.provide({
provide: ActivatedRoute,
useValue: new mockActivatedRoute(),
})
);
beforeAll(() => {
mockGetWorkerJobStatus.mockImplementation(
(): Observable<WorkerJobStatus> =>
of({
worker: '',
job: '',
statusDate: new Date(),
expiration: new Date(),
jobStatus: '',
})
);
mockSetJobId.mockImplementation((jobId) =>
console.debug('mockSetJobId', jobId)
);
mockSetStatus.mockImplementation((status) =>
console.debug('mockSetStatus', status)
);
});
it('should create', () => {
const fixture = MockRender(WorkerJobStatusDetailContainerComponent);
expect(fixture.componentInstance).toBeTruthy();
});
it('should load job status', () => {
const jobId = 'worker|job';
const expected: WorkerJobStatus = {
worker: 'dummy-worker',
job: 'dummy-job',
expiration: randomDate(),
jobStatus: `dummy-job-status`,
statusDate: randomDate(),
};
mockGetWorkerJobStatus.mockReturnValue(of(expected));
mockParams = of({ jobId });
MockRender(WorkerJobStatusDetailContainerComponent);
console.debug(
'mockGetWorkerJobStatus calls',
mockGetWorkerJobStatus.mock.calls
);
console.debug('mockSetJobId calls', mockSetJobId.mock.calls);
console.debug('mockSetStatus calls', mockSetStatus.mock.calls);
expect(mockGetWorkerJobStatus).toHaveBeenCalledWith(jobId);
expect(mockSetJobId).toHaveBeenCalledWith(jobId);
expect(mockSetStatus).toHaveBeenCalledWith(expected);
});
}); When I run the test, the mocked ComponentStore instance is not being injected into the component. Instead it appears that an unmocked instance is being inserted. What am I doing wrong? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 3 replies
-
{
setJobId: mockSetJobId,
setStatus: mockSetStatus,
} I assume you need MockBuilder(WorkerJobStatusDetailContainerComponent)
.provide(MockProvider(WorkerJobStatusService, { getWorkerJobStatus: mockGetWorkerJobStatus })
.provide(MockProvider(WorkerJobStatusDetailStore, {
setJobId: mockSetJobId,
setStatus: mockSetStatus,
}))
.provide(MockProvider(ActivatedRoute, new mockActivatedRoute()); |
Beta Was this translation helpful? Give feedback.
MockBuilder.provide()
doesn't mock, but simply adds a provider toTestBed
, so in your exampleWorkerJobStatusDetailStore
is exactly an object with 2 properties:I assume you need
MockProvider
, thenWorkerJobStatusDetailStore
is a mock with customized properties: