This is a sample code of resolver and i want to test this with the jest on nestJS.
#Resolver()
export class UserResolver {
constructor(private readonly userService: UserService) {}
#UseGuards(GqlAccessGuard)
#Query(() => User)
async fetchUser(#CurrentUser() currentUser: ICurrentUser) {
return this.userService.findUserById({ id: currentUser.id });
}
#Mutation(() => User)
async createUser(#Args('createUserInput') createUserInput: CreateUserInput) {
return this.userService.create(createUserInput);
}
}
When I'm trying to test the "fetchUser" api of this resolver I'm stucked with the #UseGuard(). I don't know how can i import or provide the 'GQlAccessGuard' into the test code. Since I use the NestJs to build Graphql-codefirst server I used custom guard that extends AuthGuards to convert Context that request has.
export class GqlAccessGuard extends AuthGuard('access') {
getRequest(context: ExecutionContext) {
const ctx = GqlExecutionContext.create(context);
return ctx.getContext().req;
}
}
#Injectable()
export class JwtAccessStrategy extends PassportStrategy(Strategy, 'access') {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: 'jwt-access-token-key',
});
}
async validate(payload: any) {
return {
id: payload.sub,
email: payload.email,
role: payload.role,
};
}
}
const createUserInput: CreateUserInput = {
email: 'test#gmail.com',
name: 'test',
password: 'testpwd',
phone: '010-1234-5678',
role: Role.USER,
};
class MockGqlGuard extends AuthGuard('access') {
getRequest(context: ExecutionContext) {
const ctx = GqlExecutionContext.create(context);
return ctx.getContext().req;
}
}
describe('UserResolver', () => {
let userResolver: UserResolver;
beforeEach(async () => {
const moduleRef = await Test.createTestingModule({
imports: [JwtModule.register({})],
providers: [UserResolver, JwtAccessStrategy],
})
.useMocker((token) => {
if (token === UserService) {
return {
create: jest.fn().mockReturnValue({
id: 'testuuid',
email: 'test#gmail.com',
name: 'test',
password: 'testpwd',
phone: '010-1234-5678',
role: Role.USER,
}),
};
}
})
.overrideGuard(GqlAccessGuard)
.useValue(MockGqlGuard)
.compile();
userResolver = moduleRef.get<UserResolver>(UserResolver);
});
describe('create', () => {
it('should return user created', async () => {
const result: User = {
id: 'testuuid',
email: 'test#gmail.com',
name: 'test',
password: 'testpwd',
phone: '010-1234-5678',
role: Role.USER,
};
expect(await userResolver.createUser(createUserInput)).toStrictEqual(
result,
);
});
});
});
I'm so curious about this and spent several days to search about it. also want to know how can i deal with the customized decorator(createParamDecorator that i made) to use on the test code.
please help me on this and provide me with some references.
Related
When I tried with basic repository provided typeorm, I think test is completed.
But to do 'unit test' with custom repository of typeorm is not working.
I think mocking custom repository has problem.
What I have to do for mocking custom repostitory?
Next are test file and source file to test.
Thanks.
quests.service.spec.ts
import { Test } from '#nestjs/testing';
import { getRepositoryToken } from '#nestjs/typeorm';
import { QuestsService } from 'src/quests/quests.service';
import { QuestRepository } from 'src/quests/repositories/quest.repository';
import { Repository } from 'typeorm';
import { Complete } from 'src/quests/entities/complete.entity';
import { Player } from 'src/players/entities/player.entity';
const mockRepository = () => ({
create: jest.fn(),
save: jest.fn(),
findOne: jest.fn(),
});
const mockQuestsRepository = {
save: jest.fn(),
findOne: jest.fn(),
findAllWithCompletes: jest.fn(),
findOneWithCompletes: jest.fn(),
};
type MockRepository<T = any> = Partial<Record<keyof Repository<T>, jest.Mock>>;
type MockQuestRepository = Partial<Record<keyof QuestRepository, jest.Mock>>;
describe('QuestsService', () => {
let service: QuestsService;
let playersRepository: MockRepository<Player>;
let completeRepository: MockRepository<Complete>;
let questsRepository: MockQuestRepository;
beforeAll(async () => {
const module = await Test.createTestingModule({
providers: [
QuestsService,
{
provide: getRepositoryToken(Player),
useValue: mockRepository(),
},
{
provide: getRepositoryToken(Complete),
useValue: mockRepository(),
},
{
provide: QuestRepository,
useValue: mockQuestsRepository,
},
],
}).compile();
service = module.get<QuestsService>(QuestsService);
playersRepository = module.get(getRepositoryToken(Player));
completeRepository = module.get(getRepositoryToken(Complete));
questsRepository = module.get(QuestRepository);
});
describe('questComplete', () => {
it('should fail if quest does not exist', async () => {
questsRepository.findOne.mockResolvedValue(undefined);
const result = await service.questComplete(-1, 1);
expect(result).toEqual({
ok: false,
message: 'cant find requested Quest.',
});
});
it('should fail if player does not exist', async () => {
questsRepository.findOne.mockResolvedValue(true);
playersRepository.findOne.mockResolvedValue(undefined);
const result = await service.questComplete(1, 1);
expect(result).toEqual({
ok: false,
message: 'cant find player.',
});
});
});
});
quests.service.ts
#Injectable()
export class QuestsService {
constructor(
#InjectRepository(Complete)
private readonly completes: Repository<Complete>,
private readonly quests: QuestRepository
) {}
async questComplete(questId: number, playerId: number) {
try {
const quest = await this.quests.findOne({ id: questId });
if (!quest)
return { ok: false, message: 'cant find requested Quest.' };
const player = await Player.findOne({ where: { id: playerId } });
if (!player)
return { ok: false, message: 'cant find player.' };
const isCompleted = await this.completes.findOne({ quest, player });
if (isCompleted)
return { ok: false, message: 'quest is already completed.' };
await this.completes.save(this.completes.create({ quest, player }));
return { ok: true };
} catch (error) {
return { ok: false, message: 'quest cant be completed.' };
}
}
}
according to this github repo we want to test a user sample
consider this test file :
const mockUser = (
phone = '9189993388',
password = 'jack1234',
id = '3458',
): User => ({
phone,
password,
_id: new Schema.Types.ObjectId(id),
});
const mockUserDoc = (mock?: Partial<User>): Partial<IUserDocument> => ({
phone: mock?.phone || '9189993388',
password: mock?.password || 'jack1234',
_id: mock?._id || new Schema.Types.ObjectId('3458'),
});
const userArray = [
mockUser(),
mockUser('Jack', '9364445566', 'jack#gmail.com'),
];
const userDocArray = [
mockUserDoc(),
mockUserDoc({
phone: '9364445566',
password: 'jack1234',
_id: new Schema.Types.ObjectId('342'),
}),
mockUserDoc({
phone: '9364445567',
password: 'mac$',
_id: new Schema.Types.ObjectId('425'),
}),
];
describe('UserRepository', () => {
let repo: UserRepository;
let model: Model<IUserDocument>;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
UserRepository,
{
provide: getModelToken('User'),
// notice that only the functions we call from the model are mocked
useValue: {
new: jest.fn().mockResolvedValue(mockUser()),
constructor: jest.fn().mockResolvedValue(mockUser()),
find: jest.fn(),
findOne: jest.fn(),
update: jest.fn(),
create: jest.fn(),
remove: jest.fn(),
exec: jest.fn(),
},
},
],
}).compile();
repo = module.get<UserRepository>(UserRepository);
model = module.get<Model<IUserDocument>>(getModelToken('User'));
});
it('should be defined', () => {
expect(repo).toBeDefined();
});
afterEach(() => {
jest.clearAllMocks();
});
it('should return all users', async () => {
jest.spyOn(model, 'find').mockReturnValue({
exec: jest.fn().mockResolvedValueOnce(userDocArray),
} as any);
const users = await repo.findAll({});
expect(users).toEqual(userArray);
});
it('should getOne by id', async () => {
const userId = '324';
jest.spyOn(model, 'findOne').mockReturnValueOnce(
createMock<Query<IUserDocument, IUserDocument>>({
exec: jest.fn().mockResolvedValueOnce(
mockUserDoc({
_id: new Schema.Types.ObjectId(userId),
}),
),
}) as any,
);
const findMockUser = mockUser('Tom', userId);
const foundUser = await repo.findById(new Schema.Types.ObjectId(userId));
expect(foundUser).toEqual(findMockUser);
});
and this is the user document file:
export interface IUserDocument extends Document {
_id: Schema.Types.ObjectId;
email?: string;
password: string;
firstName?: string;
lastName?: string;
nationalCode?: string;
phone: string;
address?: string;
avatar?: string;
}
the 1th and 2nd test are passed but the third one throws:
TypeError: this.userModel.findById is not a function
also the interface is extended from mongoose Document, the findById function is not recognized in the test.
this is the github repo available.
so any help will be appreciated.
Notice how in your UserModel mock you don't provide a mock function for findById
{
provide: getModelToken('User'),
// notice that only the functions we call from the model are mocked
useValue: {
new: jest.fn().mockResolvedValue(mockUser()),
constructor: jest.fn().mockResolvedValue(mockUser()),
find: jest.fn(),
findOne: jest.fn(),
update: jest.fn(),
create: jest.fn(),
remove: jest.fn(),
exec: jest.fn(),
findById: jest.fn(), // <-------------- Add this
},
},
There needs to be a findById method in that set of methods that you mock.
by the way, i needed the constructor to be called via new so i preferd to define a mock class like this:
class UserModelMock {
constructor(private data) {}
new = jest.fn().mockResolvedValue(this.data);
save = jest.fn().mockResolvedValue(this.data);
static find = jest.fn().mockResolvedValue(mockUser());
static create = jest.fn().mockResolvedValue(mockUser());
static remove = jest.fn().mockResolvedValueOnce(true);
static exists = jest.fn().mockResolvedValue(false);
static findOne = jest.fn().mockResolvedValue(mockUser());
static findByIdAndUpdate = jest.fn().mockResolvedValue(mockUser());
static findByIdAndDelete = jest.fn().mockReturnThis();
static exec = jest.fn();
static deleteOne = jest.fn().mockResolvedValue(true);
static findById = jest.fn().mockReturnThis();
}
I have the following code:
async save(id: string) {
const person = await PersonModel.findOne({
where: { id: id },
});
if (!person) {
await PersonModel.create({
id: '2345',
name: 'John Doe',
age: 25
});
return;
}
await person.increment({ age: 15 });
}
Now, I wanted to test person.increment() in which the age will be added with 15. I have the following code to escape the condition that will create a new record for the model.
const findOneFake = sinon.spy(() => {
return {}; //returns empty object or true
});
const proxy = (proxyquire('./path/to/file.ts', {
'./path/to/PersonModel.ts': {
default: {
findOne: findOneFake
}
}
})).default;
beforeEach(async () => {
await save();
});
it('should increment age with 15');
How am I going to do that? What do I do to test it? I can use sinon.fake() to PersonModel.create or PersonModel.update but I am troubled testing the instance of a Sequelize Model.
I'm trying to set up unit tests for a sample Angular5 app using AngularFire2 (version5) google provider login, My auth service is fairly simple and it looks like this:
let authState = null;
let mockAngularFireAuth: any = {authState: Observable.of(authState)};
#Injectable()
export class AuthService {
loggedIn: boolean;
private user: Observable<firebase.User>;
constructor(
public afAuth: AngularFireAuth
) {
this.user = afAuth.authState;
this.user.subscribe(
(user) => {
if (user) {
this.loggedIn = true;
} else {
this.loggedIn = false;
}
});
}
// --------------------------------- Google Login -----------------------------------
loginWithGoogle() {
// Sign in/up with google provider
firebase.auth().setPersistence(firebase.auth.Auth.Persistence.SESSION)
.then(() => {
this.afAuth.auth.signInWithPopup(new firebase.auth.GoogleAuthProvider())
.catch((error) => {
if (error.code === 'auth/account-exists-with-different-credential') {
alert('This email address is already registered');
}
});
});
}
// ------------------------- Checks User Authentication -----------------------
isAuthenticated() {
// returns true if the user is logged in
return this.loggedIn;
}
// --------------------------------- User LogOut -----------------------------------
logOut() {
this.afAuth.auth.signOut()
.then(() => {
this.loggedIn = false;
});
}
}
I want to test my loginWithGoogle() method but I am not sure where to start. So far my auth service spec file looks like this:
describe('AuthService', () => {
let authService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
AngularFireDatabaseModule,
AngularFireModule.initializeApp(environment.firebase),
RouterTestingModule
],
providers: [
{provide: AngularFireAuth, useValue: mockAngularFireAuth},
AuthService,
]
});
inject([AuthService], (service: AuthService) => {
authService = service;
})();
});
it('should be defined', () => {
expect(authService).toBeDefined();
});
it('should return true if loggedIn is true', () => {
expect(authService.isAuthenticated()).toBeFalsy();
authService.loggedIn = true;
expect(authService.isAuthenticated()).toBeTruthy();
});
});
Any help would be appreciated.
Well, this is what I did. I mocked the AngularFireAuth and returned the promise with reject or resolve promise to be caught. I am new to jasmine and testing, so feel free to correct me if I am doing something wrong.
it('should return a rejected promise', () => {
authState = {
email: 'lanchanagupta#gmail.com',
password: 'password',
};
mockAngularFireAuth = {
auth: jasmine.createSpyObj('auth', {
'signInWithPopup': Promise.reject({
code: 'auth/account-exists-with-different-credential'
}),
}),
authState: Observable.of(authState)
};
mockAngularFireAuth.auth.signInWithPopup()
.catch((error: { code: string }) => {
expect(error.code).toBe('auth/account-exists-with-different-credential');
});
});
it('should return a resolved promise', () => {
authState = {
email: 'lanchanagupta#gmail.com',
password: 'password',
uid: 'nuDdbfbhTwgkF5C6HN5DWDflpA83'
};
mockAngularFireAuth = {
auth: jasmine.createSpyObj('auth', {
'signInWithPopup': Promise.resolve({
user: authState
}),
})
};
mockAngularFireAuth.auth.signInWithPopup()
.then(data => {
expect(data['user']).toBe(authState);
});
});
I am trying to create async tests with axios-mock and jest.
This is my test file:
var axios = require('axios');
var MockAdapter = require('axios-mock-adapter');
const middlewares = [thunk,axiosMiddleware]
const mockStore = configureMockStore(middlewares)
describe('async-actions', () => {
var instance;
var mock;
beforeEach(function() {
instance = axios.create();
mock = new MockAdapter(instance);
});
afterEach(() => {
mock.reset()
mock.restore()
})
it('creates FETCH_BOOKINGS_SUCCESS when fetch bookings has been done', () => {
mock
.onGet('/bookings').reply(200, {
data: [
{ id: 1, name: 'test booking' }
]
});
const expectedActions = [
{type: "FETCH_BOOKINGS_START" },
{type: "FETCH_BOOKINGS_SUCCESS", }
]
const store = mockStore({
session: {
token: {
token: "test_token"
}
}
})
return store.dispatch(actions.fetchBookingsTest())
.then(
() => {
expect(store.getActions()).toEqual(expectedActions)
})
// return of async actions
})
})
And my action:
export function fetchBookingsTest() {
return (dispatch) => {
dispatch(async.fetchDataStart(namedType));
return dispatch(rest.get(BOOKINGS))
.then(
(data) => {
dispatch(async.fetchDataSuccess(data,namedType));
},
(error) => {
dispatch(async.fetchDataFailure(error,namedType));
}
)
}
}
I have middleware setup that uses the authentication token from the redux store for each get request. That is why I have setup "test_token" in the mock store.
When I run this test I receive the response
[{"type": "FETCH_BOOKINGS_START"}, {"payload": [Error: Network Error], "type": "FETCH_BOOKINGS_FAILURE"}]
Why am I getting a network error? Do i need to do more setup with Jest to avoid authentication with mock-axios?