I have a service file called "A" service
#Injectable()
export class AService {
constructor(
private readonly someOtherService: SomeOtherService
)
getResult(){
//works fine normally but prints undefined when i run it throught jest
console.log(this.someOtherService)
return this.someOtherService.doNothing()
}
}
Here is how i am making my testing module
const module: TestingModule = await Test.createTestingModule({
providers: [
AService
{
provide: SomeOtherService,
useValue: {
doSomething: jest.fn(),
doNothing: jest.fn()
}
}
]
}).compile()
Here is how I am running the test
describe('getSubscriptionDetails', () => {
it('should return chargebee ', async () => {
const price = await service.getResult()
expect(true).toBe(true)
})
})
The following test fails with the error cannot read property doNothing of undefined
Related
I'm Using the in memory mongoose database for create my Unit test, and I want to create a document before the tests.
My interface is:
export interface IUsers {
readonly name: string;
readonly username: string;
readonly email: string;
readonly password: string;
}
and my beforeEach is:
import { MongooseModule } from "#nestjs/mongoose";
import { Test, TestingModule } from "#nestjs/testing";
import { closeInMongodConnection, rootMongooseTestModule } from '../test-utils/mongo/MongooseTestModule';
import { User, UserSchema } from "./schemas/users.schema";
import { UsersService } from "./users.service";
describe("UsersService", () => {
let service: UsersService;
let testingModule: TestingModule;
let userModel: Model<User>;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
rootMongooseTestModule(),
MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
],
providers: [UsersService],
}).compile();
service = module.get<UsersService>(UsersService);
//create user
userModel = testingModule.get<Model<User>>(
'UserModel',
);
});
I get an error TypeError: Cannot read pro perties of undefined (reading 'get') during the test. I tried to use let userModel: Model<IUsers>; But I get the same error.
Use either testingModule or module.
You declared testingModule but never initialized.
let testingModule: TestingModule; This part is undefined unless something is assigned to it.
Try like this
describe('UsersService', () => {
let testingModule: TestingModule;
let userModel: Model<User>;
let userService: UserService;
beforeEach(async () => {
testingModule = await Test.createTestingModule({
imports: [
rootMongooseTestModule,
MongooseModule.forFeature([{ name: User.name, schema: UserSchema }])],
providers: [UsersService],
}).compile();
userService = testingModule.get<UsersService>(UsersService);
userModel = testingModule.get<Model<User>>('UserModel');
// await userModel.create(...) or whatever methods you have
});
});
I'm using vue2 with composition Api, vuex and apollo client to request a graphql API and I have problems when mocking composable functions with jest
// store-service.ts
export function apolloQueryService(): {
// do some graphql stuff
return { result, loading, error };
}
// store-module.ts
import { apolloQueryService } from 'store-service'
export StoreModule {
state: ()=> ({
result: {}
}),
actions: {
fetchData({commit}) {
const { result, loading, error } = apolloQueryService()
commit('setState', result);
}
},
mutations: {
setState(state, result): {
state.result = result
}
}
}
The Test:
// store-module.spec.ts
import { StoreModule } from store-module.ts
const store = StoreModule
describe('store-module.ts', () => {
beforeEach(() => {
jest.mock('store-service', () => ({
apolloQueryService: jest.fn().mockReturnValue({
result: { value: 'foo' }, loading: false, error: {}
})
}))
})
test('action', async ()=> {
const commit = jest.fn();
await store.actions.fetchData({ commit });
expect(commit).toHaveBeenCalledWith('setData', { value: 'foo' });
})
}
The test fails, because the commit gets called with ('setData', { value: undefined }) which is the result from the original apolloQueryService. My Mock doesn't seem to work. Am I doing something wrong? Appreciate any help, thanks!
Try this :
// store-module.spec.ts
import { StoreModule } from store-module.ts
// first mock the module. use the absolute path to store-service.ts from the project root
jest.mock('store-service');
// then you import the mocked module.
import { apolloQueryService } from 'store-service';
// finally, you add the mock return values for the mock module
apolloQueryService.mockReturnValue({
result: { value: 'foo' }, loading: false, error: {}
});
/* if the import order above creates a problem for you,
you can extract the first step (jest.mock) to an external setup file.
You should do this if you are supposed to mock it in all tests anyway.
https://jestjs.io/docs/configuration#setupfiles-array */
const store = StoreModule
describe('store-module.ts', () => {
test('action', async ()=> {
const commit = jest.fn();
await store.actions.fetchData({ commit });
expect(commit).toHaveBeenCalledWith('setData', { value: 'foo' });
})
}
I have a validation pipe to check input that works when I manually create a product(using postman), but it doesn't check when I run tests. any explanations?
my validator:
#Injectable()
export class JoiValidationPipe implements PipeTransform {
constructor(private schema: ObjectSchema) {}
transform(value: any, metadata: ArgumentMetadata) {
const { error } = this.schema.validate(value);
if (error) {
throw new HttpException('Validation failed', HttpStatus.BAD_REQUEST);
}
return value;
}
}
my controller:
#UsePipes(new JoiValidationPipe(productSchema))
#Post()
async create(#Body() createProductDto: CreateProductDto): Promise<Product> {
return (await this.productsService.create(createProductDto)).product;
}
my test:
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [ProductsController],
providers: [ProductsService],
}).compile();
controller = module.get<ProductsController>(ProductsController);
service = module.get<ProductsService>(ProductsService);
});
describe('create()', () => {
it('should fail to add a new product', async () => {
const result: Product = {
name: 'p',
price: -100,
category: 'junk',
};
expect(await controller.create(result)).toBe(result);
});
});
my schema:
export const productSchema: ObjectSchema = object({
createProductDto: object().keys({
name: string().min(5).required(),
price: number().integer().min(0).default(0),
category: string().min(5).required(),
}),
});
Pipes don't run unless you're going through the HTTP request. Same for other enhancers like guards and interceptors. If you want to test the pipe you can do that with supertest and e2e tests, or you can test the schema directly with joi in a different test suite
In my React-Native application i wanna write some unit tests for my Native Libraries.
dataStorage.js
import RNDataStorage, {ACCESSIBLE} from "react-native-data-storage";
const dataStorage = {
setData: function (key, value) {
return RNDataStorage.set(key, value, {accessible: ACCESSIBLE.ALWAYS_THIS_DEVICE_ONLY})
.then(res => {
console.log(res);
return true;
})
},
}
export default dataStorage;
dataStorage.test.js
import dataStorage from '../../src/services/dataStorage'
jest.mock('react-native-data-storage', () => {
return {
RNDataStorage: {
set: jest.fn(),
}
};
});
it('Should return Access & RefreshToken', function () {
dataStorage.setData('John', 'Test');
});
When i run this setup i receive the error: TypeError: Cannot read property 'set' of undefined.
What is the correct way to mocks some modules? Thanks for any help
The module you are mocking is an ES6 module with a default export and a named export.
Mocking it like this should get your test running:
jest.mock('react-native-data-storage', () => {
return {
__esModule: true,
default: {
set: jest.fn(() => Promise.resolve('the response'))
},
ACCESSIBLE: {
ALWAYS_THIS_DEVICE_ONLY: true
}
};
});
Answer based on this post
In one of my unit test files, I have to mock several times the same service with different mocks.
import { MyService } from '../services/myservice.service';
import { MockMyService1 } from '../mocks/mockmyservice1';
import { MockMyService2 } from '../mocks/mockmyservice2';
describe('MyComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
MyComponent
],
providers: [
{ provide: MyService, useClass: MockMyService1 }
]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MapComponent);
mapComponent = fixture.componentInstance;
fixture.detectChanges();
});
describe('MyFirstTest', () => {
it('should test with my first mock', () => {
/**
* Test with my first mock
*/
});
});
describe('MySecondTest', () => {
// Here I would like to change { provide: MyService, useClass: MockMyService1 } to { provide: MyService, useClass: MockMyService2 }
it('should test with my second mock', () => {
/**
* Test with my second mock
*/
});
});
});
I see that the function overrideProvider exists, but I did not manage to use it in my test. When I use it in a "it", the provider doesn't change. I didn't manage to find an example where this function is called. Could you explain me how to use it properly? Or have you an other method to do that?
As of angular 6 I noticed that overrideProvider works with the useValue property. So in order to make it work try something like:
class MockRequestService1 {
...
}
class MockRequestService2 {
...
}
then write you TestBed like:
// example with injected service
TestBed.configureTestingModule({
// Provide the service-under-test
providers: [
SomeService, {
provide: SomeInjectedService, useValue: {}
}
]
});
And whenever you want to override the provider just use:
TestBed.overrideProvider(SomeInjectedService, {useValue: new MockRequestService1()});
// Inject both the service-to-test and its (spy) dependency
someService = TestBed.get(SomeService);
someInjectedService = TestBed.get(SomeInjectedService);
Either in a beforeEach() function or place it in an it() function.
If you need TestBed.overrideProvider() with different values for different test cases, TestBed is frozen after call of TestBed.compileComponents() as #Benjamin Caure already pointed out. I found out that it is also frozen after call of TestBed.get().
As a solution in your 'main' describe use:
let someService: SomeService;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
{provide: TOKEN, useValue: true}
]
});
// do NOT initialize someService with TestBed.get(someService) here
}
And in your specific test cases use
describe(`when TOKEN is true`, () => {
beforeEach(() => {
someService = TestBed.get(SomeService);
});
it(...)
});
describe(`when TOKEN is false`, () => {
beforeEach(() => {
TestBed.overrideProvider(TOKEN, {useValue: false});
someService = TestBed.get(SomeService);
});
it(...)
});
If the service is injected as public property, e.g.:
#Component(...)
class MyComponent {
constructor(public myService: MyService)
}
You can do something like:
it('...', () => {
component.myService = new MockMyService2(...); // Make sure to provide MockMyService2 dependencies in constructor, if it has any.
fixture.detectChanges();
// Your test here...
})
If injected service is stored in a private property, you can write it as (component as any).myServiceMockMyService2 = new MockMyService2(...); to bypass TS.
It's not pretty but it works.
As for TestBed.overrideProvider, I had no luck with that approach (which would be much nicer if it worked):
it('...', () =>{
TestBed.overrideProvider(MyService, { useClass: MockMyService2 });
TestBed.compileComponents();
fixture = TestBed.createComponent(ConfirmationModalComponent);
component = fixture.componentInstance;
fixture.detectChanges();
// This was still using the original service, not sure what is wrong here.
});
I was facing similar problem, but in a simpler scenario, just one test(describe(...)) with multiple specifications(it(...)).
The solution that worked for me was postponing the TestBed.compileComponents and the TestBed.createComponent(MyComponent) commands.
Now I execute those on each individual test/specification, after calling TestBed.overrideProvider(...) when needed.
describe('CategoriesListComponent', () => {
...
beforeEach(async(() => {
...//mocks
TestBed.configureTestingModule({
imports: [HttpClientTestingModule, RouterTestingModule.withRoutes([])],
declarations: [CategoriesListComponent],
providers: [{provide: ActivatedRoute, useValue: mockActivatedRoute}]
});
}));
...
it('should call SetCategoryFilter when reload is false', () => {
const mockActivatedRouteOverride = {...}
TestBed.overrideProvider(ActivatedRoute, {useValue: mockActivatedRouteOverride });
TestBed.compileComponents();
fixture = TestBed.createComponent(CategoriesListComponent);
fixture.detectChanges();
expect(mockCategoryService.SetCategoryFilter).toHaveBeenCalledTimes(1);
});
Just for reference, if annynone meets this issue.
I tried to use
TestBed.overrideProvider(MockedService, {useValue: { foo: () => {} } });
it was not working, still the original service was injected in test (that with providedIn: root)
In test I used alias to import OtherService:
import { OtherService } from '#core/OtherService'`
while in the service itself I had import with relative path:
import { OtherService } from '../../../OtherService'
After correcting it so both test and service itself had same imports TestBed.overrideProvider() started to take effect.
Env: Angular 7 library - not application and jest
I needed to configure MatDialogConfig for two different test scenarios.
As others pointed out, calling compileCompents will not allow you to call overrideProviders. So my solution is to call compileComponents after calling overrideProviders:
let testConfig;
beforeEach(waitForAsync((): void => {
configuredTestingModule = TestBed.configureTestingModule({
declarations: [MyComponentUnderTest],
imports: [
MatDialogModule
],
providers: [
{ provide: MatDialogRef, useValue: {} },
{ provide: MAT_DIALOG_DATA, useValue: { testConfig } }
]
});
}));
const buildComponent = (): void => {
configuredTestingModule.compileComponents(); // <-- compileComponents here
fixture = TestBed.createComponent(MyComponentUnderTest);
component = fixture.componentInstance;
fixture.detectChanges();
};
describe('with default mat dialog config', (): void => {
it('sets the message property in the component to the default', (): void => {
buildComponent(); // <-- manually call buildComponent helper before each test, giving you more control of when it is called.
expect(compnent.message).toBe(defaultMessage);
});
});
describe('with custom config', (): void => {
const customMessage = 'Some custom message';
beforeEach((): void => {
testConfig = { customMessage };
TestBed.overrideProvider(MAT_DIALOG_DATA, { useValue: testConfig }); //< -- override here, before compiling
buildComponent();
});
it('sets the message property to the customMessage value within testConfig', (): void => {
expect(component.message).toBe(customMessage);
});
});