Reputation: 830
I have a dummy service:
export class PatientService extends EntityCollectionServiceBase<Patient> {
constructor(serviceElementsFactory: EntityCollectionServiceElementsFactory) {
super('Patient', serviceElementsFactory);
}
}
I have the following test:
describe('PatientService', () => {
let service: PatientService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(PatientService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
And it gives me:
NullInjectorError: No provider for EntityCollectionServiceElementsFactory
So I updated as follows:
describe('PatientService', () => {
let service: PatientService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [EntityDataModuleWithoutEffects.forRoot(entityConfig)],
providers: [provideMockStore()],
});
service = TestBed.inject(PatientService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
But now it gives me:
NullInjectorError: No provider for ScannedActionsSubject!
How can I properly fix my test?
Upvotes: 11
Views: 4172
Reputation: 13574
Because of changes in the latest version of ngrx
, you need to import an empty store modules and HttpClientTestingModule
.
Please check the example below:
describe('PatientService', () => {
let service: PatientService;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
PatientService, // <- your entity collection service
// a way to mock the store selectors
provideMockStore(),
],
imports: [
EntityDataModule.forRoot({
entityMetadata: {
Patient: {}, // <- change to your entity
},
pluralNames: {
Patient: 'Patients', // <- change to your entity
},
}),
// can be kept as it is if there are no more dependencies
StoreModule.forRoot({}),
EffectsModule.forRoot([]),
HttpClientTestingModule,
],
});
service = TestBed.inject(PatientService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
Yep, you are very close, do like that. You need to provide in testbed everything what is required for the configuration.
In our case it means that PatientService
should be provided as the testing unit and EntityCollectionServiceElementsFactory
should be provided as its dependency.
But EntityCollectionServiceElementsFactory
has own dependencies: EntityDispatcherFactory
, EntityDefinitionService
, EntitySelectorsFactory
and EntitySelectors$Factory
. Which we don't want to provide because they might have own dependencies making our live worse.
To fix it let's simply sub EntityCollectionServiceElementsFactory
.
describe('PatientService', () => {
let service: PatientService;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
PatientService,
{
provide: EntityCollectionServiceElementsFactory,
// our stub
useValue: {
methodsToFake: jasmine.createSpy(),
// ....
},
},
],
});
service = TestBed.inject(PatientService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
Also to avoid the pain of mocking dependencies and its methods in future you can use ng-mocks. Then the test could looks like that:
describe('PatientService', () => {
let service: PatientService;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
PatientService,
{
provide: EntityCollectionServiceElementsFactory,
useValue: MockService(EntityCollectionServiceElementsFactory),
},
],
});
service = TestBed.inject(PatientService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
Upvotes: 6
Reputation: 119
In case you just need to get rid of the errors, you can import all the required modules like this:
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule, EffectsModule.forRoot([]), StoreModule.forRoot({}), EntityDataModule.forRoot(entityConfig)],
});
service = TestBed.inject(PatientService);
});
Upvotes: 2
Reputation: 15505
You will have to setup the whole data module, as you would in a "normal Angular module".
This means, providing a store, effects, and the data entity.
There could be a better way, but... i don't know 🤷♂️
Upvotes: 0