AVt天堂网 手机版,亚洲va久久久噜噜噜久久4399,天天综合亚洲色在线精品,亚洲一级Av无码毛片久久精品

當前位置:首頁 > 科技  > 軟件

如何為 Nest.js 編寫單元測試和 E2E 測試

來源: 責編: 時間:2024-05-21 17:32:40 138觀看
導讀前言最近在給一個 nestjs 項目寫單元測試(Unit Testing)和 e2e 測試(End-to-End Testing,端到端測試,簡稱 e2e 測試),這是我第一次給后端項目寫測試,發(fā)現(xiàn)和之前給前端項目寫測試還不太一樣,導致在一開始寫測試時感覺無從下手。

前言

最近在給一個 nestjs 項目寫單元測試(Unit Testing)和 e2e 測試(End-to-End Testing,端到端測試,簡稱 e2e 測試),這是我第一次給后端項目寫測試,發(fā)現(xiàn)和之前給前端項目寫測試還不太一樣,導致在一開始寫測試時感覺無從下手。后來在看了一些示例之后才想明白怎么寫測試,所以打算寫篇文章記錄并分享一下,以幫助和我有相同困惑的人。gNh28資訊網(wǎng)——每日最新資訊28at.com

同時我也寫了一個 demo 項目,相關的單元測試、e2e 測試都寫好了,有興趣可以看一下。代碼已上傳到 Github: nestjs-interview-demo[1]。gNh28資訊網(wǎng)——每日最新資訊28at.com

單元測試和 E2E 測試的區(qū)別

單元測試和 e2e 測試都是軟件測試的方法,但它們的目標和范圍有所不同。gNh28資訊網(wǎng)——每日最新資訊28at.com

單元測試是對軟件中的最小可測試單元進行檢查和驗證。比如一個函數(shù)、一個方法都可以是一個單元。在單元測試中,你會對這個函數(shù)的各種輸入給出預期的輸出,并驗證功能的正確性。單元測試的目標是快速發(fā)現(xiàn)函數(shù)內(nèi)部的 bug,并且它們?nèi)菀拙帉憽⒖焖賵?zhí)行。gNh28資訊網(wǎng)——每日最新資訊28at.com

而 e2e 測試通常通過模擬真實用戶場景的方法來測試整個應用,例如前端通常使用瀏覽器或無頭瀏覽器來進行測試,后端則是通過模擬對 API 的調(diào)用來進行測試。gNh28資訊網(wǎng)——每日最新資訊28at.com

在 nestjs 項目中,單元測試可能會測試某個服務(service)、某個控制器(controller)的一個方法,例如測試 Users 模塊中的 update 方法是否能正確的更新一個用戶。而一個 e2e 測試可能會測試一個完整的用戶流程,如創(chuàng)建一個新用戶,然后更新他們的密碼,然后刪除該用戶。這涉及了多個服務和控制器。gNh28資訊網(wǎng)——每日最新資訊28at.com

編寫單元測試

為一個工具函數(shù)或者不涉及接口的方法編寫單元測試,是非常簡單的,你只需要考慮各種輸入并編寫相應的測試代碼就可以了。但是一旦涉及到接口,那情況就復雜了。用代碼來舉例:gNh28資訊網(wǎng)——每日最新資訊28at.com

async validateUser(  username: string,  password: string,): Promise<UserAccountDto> {  const entity = await this.usersService.findOne({ username });  if (!entity) {    throw new UnauthorizedException('User not found');  }  if (entity.lockUntil && entity.lockUntil > Date.now()) {    const diffInSeconds = Math.round((entity.lockUntil - Date.now()) / 1000);    let message = `The account is locked. Please try again in ${diffInSeconds} seconds.`;    if (diffInSeconds > 60) {      const diffInMinutes = Math.round(diffInSeconds / 60);      message = `The account is locked. Please try again in ${diffInMinutes} minutes.`;    }    throw new UnauthorizedException(message);  }  const passwordMatch = bcrypt.compareSync(password, entity.password);  if (!passwordMatch) {    // $inc update to increase failedLoginAttempts    const update = {      $inc: { failedLoginAttempts: 1 },    };    // lock account when the third try is failed    if (entity.failedLoginAttempts + 1 >= 3) {      // $set update to lock the account for 5 minutes      update['$set'] = { lockUntil: Date.now() + 5 * 60 * 1000 };    }    await this.usersService.update(entity._id, update);    throw new UnauthorizedException('Invalid password');  }  // if validation is sucessful, then reset failedLoginAttempts and lockUntil  if (    entity.failedLoginAttempts > 0 ||    (entity.lockUntil && entity.lockUntil > Date.now())  ) {    await this.usersService.update(entity._id, {      $set: { failedLoginAttempts: 0, lockUntil: null },    });  }  return { userId: entity._id, username } as UserAccountDto;}

上面的代碼是 auth.service.ts 文件里的一個方法 validateUser,主要用于驗證登錄時用戶輸入的賬號密碼是否正確。它包含的邏輯如下:gNh28資訊網(wǎng)——每日最新資訊28at.com

1.根據(jù) username 查看用戶是否存在,如果不存在則拋出 401 異常(也可以是 404 異常)2.查看用戶是否被鎖定,如果被鎖定則拋出 401 異常和相關的提示文字3.將 password 加密后和數(shù)據(jù)庫中的密碼進行對比,如果錯誤則拋出 401 異常(連續(xù)三次登錄失敗會被鎖定賬戶 5 分鐘)4.如果登錄成功,則將之前登錄失敗的計數(shù)記錄進行清空(如果有)并返回用戶 id 和 username 到下一階段gNh28資訊網(wǎng)——每日最新資訊28at.com

可以看到 validateUser 方法包含了 4 個處理邏輯,我們需要對這 4 點都編寫對應的單元測試代碼,以確定整個 validateUser 方法功能是正常的。gNh28資訊網(wǎng)——每日最新資訊28at.com

第一個測試用例

在開始編寫單元測試時,我們會遇到一個問題,findOne 方法需要和數(shù)據(jù)庫進行交互,它要通過 username 查找數(shù)據(jù)庫中是否存在對應的用戶。但如果每一個單元測試都得和數(shù)據(jù)庫進行交互,那測試起來會非常麻煩。所以可以通過 mock 假數(shù)據(jù)來實現(xiàn)這一點。gNh28資訊網(wǎng)——每日最新資訊28at.com

舉例,假如我們已經(jīng)注冊了一個 woai3c 的用戶,那么當用戶登錄時,在 validateUser 方法中能夠通過 const entity = await this.usersService.findOne({ username }); 拿到用戶數(shù)據(jù)。所以只要確保這行代碼能夠返回想要的數(shù)據(jù),即使不和數(shù)據(jù)庫交互也是沒有問題的。而這一點,我們能通過 mock 數(shù)據(jù)來實現(xiàn)。現(xiàn)在來看一下 validateUser 方法的相關測試代碼:gNh28資訊網(wǎng)——每日最新資訊28at.com

import { Test } from '@nestjs/testing';import { AuthService } from '@/modules/auth/auth.service';import { UsersService } from '@/modules/users/users.service';import { UnauthorizedException } from '@nestjs/common';import { TEST_USER_NAME, TEST_USER_PASSWORD } from '@tests/constants';describe('AuthService', () => {  let authService: AuthService; // Use the actual AuthService type  let usersService: Partial<Record<keyof UsersService, jest.Mock>>;  beforeEach(async () => {    usersService = {      findOne: jest.fn(),    };    const module = await Test.createTestingModule({      providers: [        AuthService,        {          provide: UsersService,          useValue: usersService,        },      ],    }).compile();    authService = module.get<AuthService>(AuthService);  });  describe('validateUser', () => {    it('should throw an UnauthorizedException if user is not found', async () => {      await expect(        authService.validateUser(TEST_USER_NAME, TEST_USER_PASSWORD),      ).rejects.toThrow(UnauthorizedException);    });    // other tests...  });});

我們通過調(diào)用 usersService 的 fineOne 方法來拿到用戶數(shù)據(jù),所以需要在測試代碼中 mock usersService 的 fineOne 方法:gNh28資訊網(wǎng)——每日最新資訊28at.com

beforeEach(async () => {    usersService = {      findOne: jest.fn(), // 在這里 mock findOne 方法    };    const module = await Test.createTestingModule({      providers: [        AuthService, // 真實的 AuthService,因為我們要對它的方法進行測試        {          provide: UsersService, // 用 mock 的 usersService 代替真實的 usersService           useValue: usersService,        },      ],    }).compile();    authService = module.get<AuthService>(AuthService);  });

通過使用 jest.fn() 返回一個函數(shù)來代替真實的 usersService.findOne()。如果這時調(diào)用 usersService.findOne() 將不會有任何返回值,所以第一個單元測試用例就能通過了:gNh28資訊網(wǎng)——每日最新資訊28at.com

it('should throw an UnauthorizedException if user is not found', async () => {  await expect(    authService.validateUser(TEST_USER_NAME, TEST_USER_PASSWORD),  ).rejects.toThrow(UnauthorizedException);});

因為在 validateUser 方法中調(diào)用 const entity = await this.usersService.findOne({ username }); 的 findOne 是 mock 的假函數(shù),沒有返回值,所以 validateUser 方法中的第 2-4 行代碼就能執(zhí)行到了:gNh28資訊網(wǎng)——每日最新資訊28at.com

if (!entity) {  throw new UnauthorizedException('User not found');}

拋出 401 錯誤,符合預期。gNh28資訊網(wǎng)——每日最新資訊28at.com

第二個測試用例

validateUser 方法中的第二個處理邏輯是判斷用戶是否鎖定,對應的代碼如下:gNh28資訊網(wǎng)——每日最新資訊28at.com

if (entity.lockUntil && entity.lockUntil > Date.now()) {  const diffInSeconds = Math.round((entity.lockUntil - Date.now()) / 1000);  let message = `The account is locked. Please try again in ${diffInSeconds} seconds.`;  if (diffInSeconds > 60) {    const diffInMinutes = Math.round(diffInSeconds / 60);    message = `The account is locked. Please try again in ${diffInMinutes} minutes.`;  }  throw new UnauthorizedException(message);}

可以看到如果用戶數(shù)據(jù)里有鎖定時間 lockUntil 并且鎖定結(jié)束時間大于當前時間就可以判斷當前賬戶處于鎖定狀態(tài)。所以需要 mock 一個具有 lockUntil 字段的用戶數(shù)據(jù):gNh28資訊網(wǎng)——每日最新資訊28at.com

it('should throw an UnauthorizedException if the account is locked', async () => {  const lockedUser = {    _id: TEST_USER_ID,    username: TEST_USER_NAME,    password: TEST_USER_PASSWORD,    lockUntil: Date.now() + 1000 * 60 * 5, // The account is locked for 5 minutes  };  usersService.findOne.mockResolvedValueOnce(lockedUser);  await expect(    authService.validateUser(TEST_USER_NAME, TEST_USER_PASSWORD),  ).rejects.toThrow(UnauthorizedException);});

在上面的測試代碼里,先定義了一個對象 lockedUser,這個對象里有我們想要的 lockUntil 字段,然后將它作為 findOne 的返回值,這通過 usersService.findOne.mockResolvedValueOnce(lockedUser); 實現(xiàn)。然后 validateUser 方法執(zhí)行時,里面的用戶數(shù)據(jù)就是 mock 出來的數(shù)據(jù)了,從而成功讓第二個測試用例通過。gNh28資訊網(wǎng)——每日最新資訊28at.com

單元測試覆蓋率

剩下的兩個測試用例就不寫了,原理都是一樣的。如果剩下的兩個測試不寫,那么這個 validateUser 方法的單元測試覆蓋率會是 50%,如果 4 個測試用例都寫完了,那么 validateUser 方法的單元測試覆蓋率將達到 100%。gNh28資訊網(wǎng)——每日最新資訊28at.com

單元測試覆蓋率(Code Coverage)是一個度量,用于描述應用程序代碼有多少被單元測試覆蓋或測試過。它通常表示為百分比,表示在所有可能的代碼路徑中,有多少被測試用例覆蓋。gNh28資訊網(wǎng)——每日最新資訊28at.com

單元測試覆蓋率通常包括以下幾種類型:gNh28資訊網(wǎng)——每日最新資訊28at.com

?行覆蓋率(Lines):測試覆蓋了多少代碼行。?函數(shù)覆蓋率(Funcs):測試覆蓋了多少函數(shù)或方法。?分支覆蓋率(Branch):測試覆蓋了多少代碼分支(例如,if/else 語句)。?語句覆蓋率(Stmts):測試覆蓋了多少代碼語句。gNh28資訊網(wǎng)——每日最新資訊28at.com

單元測試覆蓋率是衡量單元測試質(zhì)量的一個重要指標,但并不是唯一的指標。高的覆蓋率可以幫助檢測代碼中的錯誤,但并不能保證代碼的質(zhì)量。覆蓋率低可能意味著有未被測試的代碼,可能存在未被發(fā)現(xiàn)的錯誤。gNh28資訊網(wǎng)——每日最新資訊28at.com

下圖是 demo 項目的單元測試覆蓋率結(jié)果:gNh28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片gNh28資訊網(wǎng)——每日最新資訊28at.com

像 service 和 controller 之類的文件,單元測試覆蓋率一般盡量高點比較好,而像 module 這種文件就沒有必要寫單元測試了,也沒法寫,沒有意義。上面的圖片表示的是整個單元測試覆蓋率的總體指標,如果你想查看某個函數(shù)的測試覆蓋率,可以打開項目根目錄下的 coverage/lcov-report/index.html 文件進行查看。例如我想查看 validateUser 方法具體的測試情況:gNh28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片gNh28資訊網(wǎng)——每日最新資訊28at.com

可以看到原來 validateUser 方法的單元測試覆蓋率并不是 100%,還是有兩行代碼沒有執(zhí)行到,不過也無所謂了,不影響 4 個關鍵的處理節(jié)點,不要片面的追求高測試覆蓋率。gNh28資訊網(wǎng)——每日最新資訊28at.com

編寫E2E 測試

在單元測試中我們展示了如何為 validateUser() 的每一個功能點編寫單元測試,并且使用了 mock 數(shù)據(jù)的方法來確保每個功能點都能夠被測試到。而在 e2e 測試中,我們需要模擬真實的用戶場景,所以要連接數(shù)據(jù)庫來進行測試。因此,這次測試的 auth.service.ts 模塊里的方法都會和數(shù)據(jù)庫進行交互。gNh28資訊網(wǎng)——每日最新資訊28at.com

auth 模塊主要有以下幾個功能:gNh28資訊網(wǎng)——每日最新資訊28at.com

?注冊?登錄?刷新 token?讀取用戶信息?修改密碼?刪除用戶。gNh28資訊網(wǎng)——每日最新資訊28at.com

e2e 測試需要將這六個功能都測試一遍,從注冊開始,到刪除用戶結(jié)束。在測試時,我們可以建一個專門的測試用戶來進行測試,測試完成后再刪除這個測試用戶,這樣就不會在測試數(shù)據(jù)庫中留下無用的信息了。gNh28資訊網(wǎng)——每日最新資訊28at.com

beforeAll(async () => {  const moduleFixture: TestingModule = await Test.createTestingModule({    imports: [AppModule],  }).compile()  app = moduleFixture.createNestApplication()  await app.init()  // 執(zhí)行登錄以獲取令牌  const response = await request(app.getHttpServer())    .post('/auth/register')    .send({ username: TEST_USER_NAME, password: TEST_USER_PASSWORD })    .expect(201)  accessToken = response.body.access_token  refreshToken = response.body.refresh_token})afterAll(async () => {  await request(app.getHttpServer())    .delete('/auth/delete-user')    .set('Authorization', `Bearer ${accessToken}`)    .expect(200)  await app.close()})

beforeAll 鉤子函數(shù)將在所有測試開始之前執(zhí)行,所以我們可以在這里注冊一個測試賬號 TEST_USER_NAME。afterAll 鉤子函數(shù)將在所有測試結(jié)束之后執(zhí)行,所以在這刪除測試賬號 TEST_USER_NAME 是比較合適的,還能順便對注冊和刪除兩個功能進行測試。gNh28資訊網(wǎng)——每日最新資訊28at.com

在上一節(jié)的單元測試中,我們編寫了關于 validateUser 方法的相關單元測試。其實這個方法是在登錄時執(zhí)行的,用于驗證用戶賬號密碼是否正確。所以這一次的 e2e 測試也將使用登錄流程來展示如何編寫 e2e 測試用例。gNh28資訊網(wǎng)——每日最新資訊28at.com

整個登錄測試流程總共包含了五個小測試:gNh28資訊網(wǎng)——每日最新資訊28at.com

describe('login', () => {    it('/auth/login (POST)', () => {      // ...    })    it('/auth/login (POST) with user not found', () => {      // ...    })    it('/auth/login (POST) without username or password', async () => {      // ...    })    it('/auth/login (POST) with invalid password', () => {      // ...    })    it('/auth/login (POST) account lock after multiple failed attempts', async () => {      // ...    })  })

這五個測試分別是:gNh28資訊網(wǎng)——每日最新資訊28at.com

1.登錄成功,返回 2002.如果用戶不存在,拋出 401 異常3.如果不提供密碼或用戶名,拋出 400 異常4.使用錯誤密碼登錄,拋出 401 異常5.如果賬戶被鎖定,拋出 401 異常。gNh28資訊網(wǎng)——每日最新資訊28at.com

現(xiàn)在我們開始編寫 e2e 測試:gNh28資訊網(wǎng)——每日最新資訊28at.com

// 登錄成功it('/auth/login (POST)', () => {  return request(app.getHttpServer())    .post('/auth/login')    .send({ username: TEST_USER_NAME, password: TEST_USER_PASSWORD })    .expect(200)})// 如果用戶不存在,應該拋出 401 異常it('/auth/login (POST) with user not found', () => {  return request(app.getHttpServer())    .post('/auth/login')    .send({ username: TEST_USER_NAME2, password: TEST_USER_PASSWORD })    .expect(401) // Expect an unauthorized error})

e2e 的測試代碼寫起來比較簡單,直接調(diào)用接口,然后驗證結(jié)果就可以了。比如登錄成功測試,我們只要驗證返回結(jié)果是否是 200 即可。gNh28資訊網(wǎng)——每日最新資訊28at.com

前面四個測試都比較簡單,現(xiàn)在我們看一個稍微復雜點的 e2e 測試,即驗證賬戶是否被鎖定。gNh28資訊網(wǎng)——每日最新資訊28at.com

it('/auth/login (POST) account lock after multiple failed attempts', async () => {  const moduleFixture: TestingModule = await Test.createTestingModule({    imports: [AppModule],  }).compile()  const app = moduleFixture.createNestApplication()  await app.init()  const registerResponse = await request(app.getHttpServer())    .post('/auth/register')    .send({ username: TEST_USER_NAME2, password: TEST_USER_PASSWORD })  const accessToken = registerResponse.body.access_token  const maxLoginAttempts = 3 // lock user when the third try is failed  for (let i = 0; i < maxLoginAttempts; i++) {    await request(app.getHttpServer())      .post('/auth/login')      .send({ username: TEST_USER_NAME2, password: 'InvalidPassword' })  }  // The account is locked after the third failed login attempt  await request(app.getHttpServer())    .post('/auth/login')    .send({ username: TEST_USER_NAME2, password: TEST_USER_PASSWORD })    .then((res) => {      expect(res.body.message).toContain(        'The account is locked. Please try again in 5 minutes.',      )    })  await request(app.getHttpServer())    .delete('/auth/delete-user')    .set('Authorization', `Bearer ${accessToken}`)  await app.close()})

當用戶連續(xù)三次登錄失敗的時候,賬戶就會被鎖定。所以在這個測試里,我們不能使用測試賬號 TEST_USER_NAME,因為測試成功的話這個賬戶就會被鎖定,無法繼續(xù)進行下面的測試了。我們需要再注冊一個新用戶 TEST_USER_NAME2,專門用來測試賬戶鎖定,測試成功后再刪除這個用戶。所以你可以看到這個 e2e 測試的代碼非常多,需要做大量的前置、后置工作,其實真正的測試代碼就這幾行:gNh28資訊網(wǎng)——每日最新資訊28at.com

// 連續(xù)三次登錄for (let i = 0; i < maxLoginAttempts; i++) {  await request(app.getHttpServer())    .post('/auth/login')    .send({ username: TEST_USER_NAME2, password: 'InvalidPassword' })}// 測試賬號是否被鎖定await request(app.getHttpServer())  .post('/auth/login')  .send({ username: TEST_USER_NAME2, password: TEST_USER_PASSWORD })  .then((res) => {    expect(res.body.message).toContain(      'The account is locked. Please try again in 5 minutes.',    )  })

可以看到編寫 e2e 測試代碼還是相對比較簡單的,不需要考慮 mock 數(shù)據(jù),不需要考慮測試覆蓋率,只要整個系統(tǒng)流程的運轉(zhuǎn)情況符合預期就可以了。gNh28資訊網(wǎng)——每日最新資訊28at.com

應不應該寫測試

如果有條件的話,我是比較建議大家寫測試的。因為寫測試可以提高系統(tǒng)的健壯性、可維護性和開發(fā)效率。gNh28資訊網(wǎng)——每日最新資訊28at.com

提高系統(tǒng)健壯性

我們一般編寫代碼時,會關注于正常輸入下的程序流程,確保核心功能正常運作。但是一些邊緣情況,比如異常的輸入,這些我們可能會經(jīng)常忽略掉。但當我們開始編寫測試時,情況就不一樣了,這會逼迫你去考慮如何處理并提供相應的反饋,從而避免程序崩潰。可以說寫測試實際上是在間接地提高系統(tǒng)健壯性。gNh28資訊網(wǎng)——每日最新資訊28at.com

提高可維護性

當你接手一個新項目時,如果項目包含完善的測試,那將會是一件很幸福的事情。它們就像是項目的指南,幫你快速把握各個功能點。只看測試代碼就能夠輕松地了解每個功能的預期行為和邊界條件,而不用你逐行的去查看每個功能的代碼。gNh28資訊網(wǎng)——每日最新資訊28at.com

提高開發(fā)效率

想象一下,一個長時間未更新的項目突然接到了新需求。改了代碼后,你可能會擔心引入 bug,如果沒有測試,那就需要重新手動測試整個項目——浪費時間,效率低下。而有了完整的測試,一條命令就能得知代碼更改有沒有影響現(xiàn)有功能。即使出錯了,也能夠快速定位,找到問題點。gNh28資訊網(wǎng)——每日最新資訊28at.com

什么時候不建議寫測試?

短期項目、需求迭代非常快的項目不建議寫測試。比如某些活動項目,活動結(jié)束就沒用了,這種項目就不需要寫測試。另外,需求迭代非常快的項目也不要寫測試,我剛才說寫測試能提高開發(fā)效率是有前提條件的,就是功能迭代比較慢的情況下,寫測試才能提高開發(fā)效率。如果你的功能今天剛寫完,隔一兩天就需求變更了要改功能,那相關的測試代碼都得重寫。所以干脆就別寫了,靠團隊里的測試人員測試就行了,因為寫測試是非常耗時間的,沒必要自討苦吃。gNh28資訊網(wǎng)——每日最新資訊28at.com

根據(jù)我的經(jīng)驗來看,國內(nèi)的絕大多數(shù)項目(尤其是政企類項目,這種項目你說要寫測試我都想笑)都是沒有必要寫測試的,因為需求迭代太快,還老是推翻之前的需求,代碼都得加班寫,那有閑情逸致寫測試。gNh28資訊網(wǎng)——每日最新資訊28at.com

總結(jié)

在細致地講解了如何為 Nestjs 項目編寫單元測試及 e2e 測試之后,我還是想重申一下測試的重要性,它能夠提高系統(tǒng)的健壯性、可維護性和開發(fā)效率。如果沒有機會寫測試,我建議大家可以自己搞個練習項目來寫,或者說參加一些開源項目,給這些項目貢獻代碼,因為開源項目對于代碼要求一般都比較嚴格。貢獻代碼可能需要編寫新的測試用例或修改現(xiàn)有的測試用例。gNh28資訊網(wǎng)——每日最新資訊28at.com

參考資料

NestJS[14]: A framework for building efficient, scalable Node.js server-side applications.gNh28資訊網(wǎng)——每日最新資訊28at.com

MongoDB[15]: A NoSQL database used for data storage.gNh28資訊網(wǎng)——每日最新資訊28at.com

Jest[16]: A testing framework for JavaScript and TypeScript.gNh28資訊網(wǎng)——每日最新資訊28at.com

Supertest[17]: A library for testing HTTP servers.gNh28資訊網(wǎng)——每日最新資訊28at.com

gNh28資訊網(wǎng)——每日最新資訊28at.com

References

gNh28資訊網(wǎng)——每日最新資訊28at.com

[1] nestjs-interview-demo: https://github.com/woai3c/nestjs-interview-demogNh28資訊網(wǎng)——每日最新資訊28at.com

[2] 帶你入門前端工程: https://woai3c.github.io/introduction-to-front-end-engineering/gNh28資訊網(wǎng)——每日最新資訊28at.com

[3] 從零開始實現(xiàn)一個玩具版瀏覽器渲染引擎: https://github.com/woai3c/Front-end-articles/issues/44gNh28資訊網(wǎng)——每日最新資訊28at.com

[4] 手把手教你寫一個簡易的微前端框架: https://github.com/woai3c/Front-end-articles/issues/31gNh28資訊網(wǎng)——每日最新資訊28at.com

[5] 前端監(jiān)控 SDK 的一些技術要點原理分析: https://github.com/woai3c/Front-end-articles/issues/26gNh28資訊網(wǎng)——每日最新資訊28at.com

[6] 可視化拖拽組件庫一些技術要點原理分析: https://github.com/woai3c/Front-end-articles/issues/19gNh28資訊網(wǎng)——每日最新資訊28at.com

[7] 可視化拖拽組件庫一些技術要點原理分析(二): https://github.com/woai3c/Front-end-articles/issues/20gNh28資訊網(wǎng)——每日最新資訊28at.com

[8] 可視化拖拽組件庫一些技術要點原理分析(三): https://github.com/woai3c/Front-end-articles/issues/21gNh28資訊網(wǎng)——每日最新資訊28at.com

[9] 可視化拖拽組件庫一些技術要點原理分析(四): https://github.com/woai3c/Front-end-articles/issues/33gNh28資訊網(wǎng)——每日最新資訊28at.com

[10] 低代碼與大語言模型的探索實踐: https://github.com/woai3c/Front-end-articles/issues/45gNh28資訊網(wǎng)——每日最新資訊28at.com

[11] 前端性能優(yōu)化 24 條建議(2020): https://github.com/woai3c/Front-end-articles/blob/master/performance.mdgNh28資訊網(wǎng)——每日最新資訊28at.com

[12] 手把手教你寫一個腳手架: https://github.com/woai3c/Front-end-articles/issues/22gNh28資訊網(wǎng)——每日最新資訊28at.com

[13] 手把手教你寫一個腳手架(二): https://github.com/woai3c/Front-end-articles/issues/23gNh28資訊網(wǎng)——每日最新資訊28at.com

[14] NestJS: https://nestjs.com/gNh28資訊網(wǎng)——每日最新資訊28at.com

[15] MongoDB: https://www.mongodb.com/gNh28資訊網(wǎng)——每日最新資訊28at.com

[16] Jest: https://jestjs.io/gNh28資訊網(wǎng)——每日最新資訊28at.com

[17] Supertest: https://github.com/visionmedia/supertestgNh28資訊網(wǎng)——每日最新資訊28at.com

本文鏈接:http://www.tebozhan.com/showinfo-26-89714-0.html如何為 Nest.js 編寫單元測試和 E2E 測試

聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 分享能提高開發(fā)效率,提高代碼質(zhì)量的八個前端裝飾器函數(shù)

下一篇: 如此絲滑的API設計,用起來真香

標簽:
  • 熱門焦點
Top