iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 10
0
Modern Web

Nest.js framework 30天初探系列 第 10

Nestjs framework 30天初探:Day10 Unit Test & E2E Test

Unit Test

單元測試(Unit Test) 是蠻重要的一件事情,對於追求品質的工程師來說,早早點開這個科技樹,省得日後接大案寫爛code寫到懷疑人生
Nestjs本身可以使用第三方測試模組外(jasmineJest),也有內建測試模組,加上Nestjs的架構是朝SOLID原則去發展,用Nestjs去開發的專案,耦合程度會較低,寫起單元測試也較容易些。

1.0 請安裝Jest

npm install --save-dev ts-jest @types/jest

1.1 根目錄新增jest.json並修改package.json,程式碼如下。
jest.json

{
    "moduleFileExtensions": [
          "ts",
          "tsx",
          "js",
          "json"
      ],
      "transform": {
          "^.+\\.tsx?$": "<rootDir>/node_modules/ts-jest/preprocessor.js"
      },
      "testRegex": "/src/.*\\.(test|spec).(ts|tsx|js)$",
      "collectCoverageFrom" : ["src/**/*.{js,jsx,tsx,ts}", "!**/node_modules/**", "!**/vendor/**"],
      "coverageReporters": ["json", "lcov"]
  }

package.json

"scripts": {
    "start": "node index.js",
    "start:watch": "nodemon",
    "prestart:prod": "tsc",
    "start:prod": "node dist/server.js",
    "test": "jest --config=jest.json"
  },

1.2 在Users資料夾新增users.controller.spec.ts,程式碼如下。
cmd 指令:

cd src/modules/Users

src/modules/Users/users.controller.spec.ts

import { UsersController } from './users.controller';
import { UsersService } from './Services/users.service';
import { ProductsService } from '../Products/Services/products.service';
import { Request, Response, Next } from '@nestjs/common';

describe('UsersController', () => {
    
    let usersController: UsersController;
    let usersService: UsersService;
    let productService: ProductsService;

    beforeEach(() => {
        usersService = new UsersService();
        usersController = new UsersController(usersService, productService);
    });

    describe('runTest', () => {
        it('should return an array of users', () => {
            //預期回傳的資料
            const result = [
                { "_id": 1, "_name": "Michael", "_age": 25 },
                { "_id": 2, "_name": "Mary", "_age": 27 }
            ];
            //jest會監視usersService.(),並給予假資料 result
            jest.spyOn(usersService, 'getAllUsers').mockImplementation(() => result);

            usersController.getAllUsers(Request, Response, Next).then((data) => {
                //預期data會跟result一樣
                expect(data).toBe(result);
            })
        })
    })
})

1.3 暫時修改一下UsersController,註解掉response動作,程式碼如下。
src/modules/Users/users.controller.ts

@Get('users')
    @Roles('admin')
    //使用Express的參數
    async getAllUsers( @Request() req, @Response() res, @Next() next) {
        //Promise 有then catch方法可以調用
        await this.userService.getAllUsers()
            .then((users) => {
                //多種Http的Status可以使用
                //res.status(HttpStatus.OK).json(users);
            })
            .catch((error) => {
                console.error(error);
                //res.status(HttpStatus.INTERNAL_SERVER_ERROR);
            })
    }

1.3 實際跑一次單元測試,console結果如下。
cmd指令:

npm run test

https://ithelp.ithome.com.tw/upload/images/20171213/2010719576eZTd7RH0.png

說明:看到綠色打勾真爽,此為測試結果與預期一樣。

1.4 Nestjs 有Testing class可以幫助我們做單元測試,我們來使用一下,程式碼如下:
src/modules/Users/users.controller.spec.ts

import { UsersController } from './users.controller';
import { UsersService } from './Services/users.service';
import { ProductsService } from '../Products/Services/products.service';
import { Request, Response, Next } from '@nestjs/common';
import { Test } from '@nestjs/testing';

describe('UsersController', () => {

    let usersController: UsersController;
    let usersService: UsersService;
    let productService: ProductsService;
    
    //使用Nest Testing class
    beforeEach(async () => {
        const module = await Test.createTestingModule({
            controllers: [UsersController],
            components: [UsersService, ProductsService],
        }).compile();

        usersService = module.get<UsersService>(UsersService);
        usersController = module.get<UsersController>(UsersController);
    });

    describe('runTest', () => {
        it('should return an array of users', () => {
            //預期回傳的資料
            const result = [
                { "_id": 1, "_name": "Michael", "_age": 25 },
                { "_id": 2, "_name": "Mary", "_age": 27 }
            ];
            //jest會監視usersService.(),並給予假資料 result
            jest.spyOn(usersService, 'getAllUsers').mockImplementation(() => result);

            usersController.getAllUsers(Request, Response, Next).then((data) => {
                //預期data會跟result一樣
                expect(data).toBe(result);
            })
        })
    })
})

1.5 看一下測試結果,console結果如下。
cmd指令:

npm test

https://ithelp.ithome.com.tw/upload/images/20171213/201071954EjpxNjsAs.png

說明:同1.3的運行結果。

E2E Testing

E2E Testing,俗稱端對端測試,隨著專案日益龐大,可能已經有無數個Restful API,每一隻Restful API是否正常運作?光手動測試就累死人了,所以可以寫好E2E Testing,直接跑測試,是否正常運作一目了然。

2.0 持續使用Jest和Nestjs原生Testing class,但要另外安裝一下supertest,接著請在根目錄新增e2e資料夾,並在e2e資料夾底下新增jest-e2e.json、Users資料夾,然後在Users資料夾裡新增users.controller.e2e-spec.ts,程式碼如下。
cmd指令

npm install --save-dev supertest & mkdir e2e & cd e2e & mkdir Users

e2e/jest-e2e.json

{
    "moduleFileExtensions": [
        "ts",
        "tsx",
        "js",
        "json"
    ],
    "transform": {
        "^.+\\.tsx?$": "<rootDir>/../node_modules/ts-jest/preprocessor.js"
    },
    "testRegex": "/e2e/.*\\.(e2e-test|e2e-spec).(ts|tsx|js)$",
    "collectCoverageFrom" : ["src/**/*.{js,jsx,tsx,ts}", "!**/node_modules/**", "!**/vendor/**"],
    "coverageReporters": ["json", "lcov"]
}

package.json

"scripts": {
    "start": "node index.js",
    "start:watch": "nodemon",
    "prestart:prod": "tsc",
    "start:prod": "node dist/server.js",
    "test": "jest",
    "e2e": "jest --config=e2e/jest-e2e.json --forceExit"
  },

e2e/Users/users.controller.e2e-spec.ts

import * as express from 'express';
import * as request from 'supertest';
import { Test } from '@nestjs/testing';
import { UsersModule } from '../../src/modules/Users/users.module';
import { UsersService } from '../../src/modules/Users/Services/users.service';

describe('Users', () => {
    const server = express();
    //預期資料,測試要用
    const usersService = {
        getAllUsers: () => [
            { "_id": 1, "_name": "Michael", "_age": 25 },
            { "_id": 2, "_name": "Mary", "_age": 27 }
        ]
    };
    //使用Testing class
    beforeAll(async () => {
        const module = await Test.createTestingModule({
            modules: [UsersModule]
        })
            .overrideComponent(usersService).useValue(usersService)
            .compile()

        const app = module.createNestApplication(server);
        await app.init();
    });
    //測試你的Restful API
    it('/GET users',()=>{
        return request(server)
        .get('/users')
        //狀態碼
        .expect(200)
        //檢驗是否符合預期資料
        .expect(usersService.getAllUsers())
    })
});

2.1 復原UsersController的getAllUsers(),做E2E測試需要有Response,程式碼如下。
src/modules/Users/users.controller.ts

@Get('users')
    @Roles('admin')
    //使用Express的參數
    async getAllUsers( @Request() req, @Response() res, @Next() next) {
        //Promise 有then catch方法可以調用
        await this.userService.getAllUsers()
            .then((users) => {
                //多種Http的Status可以使用
                res.status(HttpStatus.OK).json(users);
            })
            .catch((error) => {
                console.error(error);
                res.status(HttpStatus.INTERNAL_SERVER_ERROR);
            })
    }

2.2 實際跑一次E2E測試,結果如下。
cmd指令

npm run e2e

console畫面
https://ithelp.ithome.com.tw/upload/images/20171213/20107195TiW7pqwgSA.png

E2E測試成功,綠色爽感十足^^。

程式碼都在github


上一篇
Nestjs framework 30天初探:Day09 Interceptors
下一篇
Nestjs framework 30天初探:Day11 WebSocket-Gateways
系列文
Nest.js framework 30天初探30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言