spectator
spectator copied to clipboard
Mocking of abstract services
I'm submitting a...
[ ] Regression (a behavior that used to work and stopped working in a new release)
[ ] Bug report
[ ] Performance issue
[x] Feature request
[ ] Documentation issue or request
[ ] Support request
[ ] Other... Please describe:
Current behavior
At the moment when passing an abstract type to the mocks array the following error is shown when running ng test
Cannot assign an abstract constructor type to a non-abstract constructor type.
The only working methods I have found so far to mock this are:
manually creating a jasmine spy
createComponentFactory({
...
providers: [
{provide: AbstractType, useValue: jasmine.createSpyObj(AbstractType.name, ['methodToMock'])}
]
})
Problem here is that later in my test I have to the jasmine way to use the mock instead of the spectator way. Using the spectator way results in an error.
jasmine way
spectator.get(AbstractType).methodToMock.and.returnValue()
spectator way
// Throws error 'andReturn does not exist'
spectator.get(AbstractType).methodToMock.andReturn()
Problem with this solution is that I have to remember which mock was created how and have to use one way or the other. This might lead to confusion when a different team member is extending my test.
Implement the abstract class and pass the implementation to the mocks array.
class ConcreteType extends AbstractType {
...
}
createComponentFactory({
...
mocks: [
ConcreteType
]
})
Problems
- For abstract types with many methods the test gets polluted with unnecessary code (all abstract methods need to be "implemented"
- The implementation has to be done in every test or a central MockImplementation must be provided.
- Both solutions aren't ideal because either we get a lot of duplicated code or every team member must know that the MockImplementation exists.
Expected behavior
- The
mocksarray can handle abstract types OR createSpyObject()can create mocks of abstract types OR- other solution
What is the motivation / use case for changing the behavior?
Consistent less error prone tests.
Environment
NOT RELEVANT
I just figured out that the second solution from the description above Implement the abstract class and pass the implementation to the mocks array. isn't working at all.
NullInjectorError: StaticInjectorError(DynamicTestModule)[MyComponent -> AbstractType]:
StaticInjectorError(Platform: core)[MyComponent -> AbstractType]:
NullInjectorError: No provider for AbstractType!
EDIT1: The following code works instead which is even worse because it needs an implementation of the abstract type and a manual entry in the providers array.
class ConcreteType extends AbstractType {
...
}
createComponentFactory({
...
providers: [
{provide: AbstractType, useValue: createSpyObject(ConcreteType)}
]
})
The options available are in this file:
https://github.com/ngneat/spectator/blob/f0e5d7a887114cfe6e1c2f42b2c6e0629d60c7c9/projects/spectator/test/injection-and-mocking.spec.ts#L41
Which means there is no option for mocking of abstract services?
The provided example only shows how to provide an implementation for an abstract service. The provided service (in the example) is not a jasmine spy so I cannot mock its behaviour.
spectator.get(AbstractQueryService).someMethod.andReturn(...);
This is also applicable to mocking said providers, e.g. mockProvider(AbstractQueryService) will fail with the same error, thus forcing me to have a stub class in my test files instead of automatically having a stubbed provider.