mirror of
https://github.com/hantsy/nestjs-rest-sample.git
synced 2025-12-08 20:36:27 +00:00
docs: update testing.md
This commit is contained in:
parent
7cddd436d8
commit
c55e19b812
123
docs/testing.md
123
docs/testing.md
@ -67,6 +67,129 @@ Test.createTestingModule({
|
||||
.override(...)
|
||||
```
|
||||
|
||||
## Jest Tips and Tricks
|
||||
|
||||
Nestjs testing is heavily dependent on Jest framework. I have spent a lot of time to research testing all components in Nestjs applications.
|
||||
|
||||
### Mocking external classes or functions
|
||||
|
||||
For example the `mongoose.connect` will require a real mongo server to connect, to mock the `createConnection` of `mongoose`.
|
||||
|
||||
Set up mocks before importing it.
|
||||
|
||||
```typescript
|
||||
jest.mock('mongoose', () => ({
|
||||
createConnection: jest.fn().mockImplementation(
|
||||
(uri:any, options:any)=>({} as any)
|
||||
),
|
||||
Connection: jest.fn()
|
||||
}))
|
||||
|
||||
//...
|
||||
import { Connection, createConnection } from 'mongoose';
|
||||
//
|
||||
```
|
||||
When a database provider is instantized, assert the `createConnection` is called.
|
||||
|
||||
```typescript
|
||||
it('connect is called', () => {
|
||||
//expect(conn).toBeDefined();
|
||||
//expect(createConnection).toHaveBeenCalledTimes(1); // it is 2 here. why?
|
||||
expect(createConnection).toHaveBeenCalledWith("mongodb://localhost/blog", {
|
||||
useNewUrlParser: true,
|
||||
useUnifiedTopology: true,
|
||||
//see: https://mongoosejs.com/docs/deprecations.html#findandmodify
|
||||
useFindAndModify: false
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
### Mock parent classes through prototype
|
||||
|
||||
Have a look at the local auth guard tests.
|
||||
|
||||
Mock the method `canActivate` in the parent prototype.
|
||||
|
||||
```typescript
|
||||
describe('LocalAuthGuard', () => {
|
||||
let guard: LocalAuthGuard;
|
||||
beforeEach(() => {
|
||||
guard = new LocalAuthGuard();
|
||||
});
|
||||
it('should be defined', () => {
|
||||
expect(guard).toBeDefined();
|
||||
});
|
||||
it('should return true for `canActivate`', async () => {
|
||||
AuthGuard('local').prototype.canActivate = jest.fn(() =>
|
||||
Promise.resolve(true),
|
||||
);
|
||||
AuthGuard('local').prototype.logIn = jest.fn(() => Promise.resolve());
|
||||
expect(await guard.canActivate({} as ExecutionContext)).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Extract the functionality into functions as possible
|
||||
|
||||
Let's have a look at the `user.model.ts`. Extract the pre `save` hook method and custom `comparePassword` method into standalone functions.
|
||||
|
||||
```typescript
|
||||
async function preSaveHook(next) {
|
||||
|
||||
// Only run this function if password was modified
|
||||
if (!this.isModified('password')) return next();
|
||||
|
||||
// Hash the password
|
||||
const password = await hash(this.password, 12);
|
||||
this.set('password', password);
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
UserSchema.pre<User>('save', preSaveHook);
|
||||
|
||||
function comparePasswordMethod(password: string): Observable<boolean> {
|
||||
return from(compare(password, this.password));
|
||||
}
|
||||
|
||||
UserSchema.methods.comparePassword = comparePasswordMethod;
|
||||
```
|
||||
|
||||
It is easy to test them like simple functions.
|
||||
|
||||
```typescript
|
||||
describe('preSaveHook', () => {
|
||||
test('should execute next middleware when password is not modified', async () => {
|
||||
const nextMock = jest.fn();
|
||||
const contextMock = {
|
||||
isModified: jest.fn()
|
||||
};
|
||||
contextMock.isModified.mockReturnValueOnce(false);
|
||||
await preSaveHook.call(contextMock, nextMock);
|
||||
expect(contextMock.isModified).toBeCalledWith('password');
|
||||
expect(nextMock).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
test('should set password when password is modified', async () => {
|
||||
const nextMock = jest.fn();
|
||||
const contextMock = {
|
||||
isModified: jest.fn(),
|
||||
set: jest.fn(),
|
||||
password: '123456'
|
||||
};
|
||||
contextMock.isModified.mockReturnValueOnce(true);
|
||||
await preSaveHook.call(contextMock, nextMock);
|
||||
expect(contextMock.isModified).toBeCalledWith('password');
|
||||
expect(nextMock).toBeCalledTimes(1);
|
||||
expect(contextMock.set).toBeCalledTimes(1);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
|
||||
## End-to-end testing
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user