Rumah > hujung hadapan web > tutorial js > Cara menulis ujian unit dan Etest untuk aplikasi NestJS

Cara menulis ujian unit dan Etest untuk aplikasi NestJS

Mary-Kate Olsen
Lepaskan: 2025-01-13 06:22:43
asal
348 orang telah melayarinya

pengenalan

Baru-baru ini, saya telah menulis ujian unit dan ujian E2E untuk projek NestJS. Ini adalah kali pertama saya menulis ujian untuk projek bahagian belakang, dan saya mendapati prosesnya berbeza daripada pengalaman saya dengan ujian bahagian hadapan, menjadikannya mencabar untuk bermula. Selepas melihat beberapa contoh, saya telah mendapat pemahaman yang lebih jelas tentang cara mendekati ujian, jadi saya bercadang untuk menulis artikel untuk merekod dan berkongsi pembelajaran saya untuk membantu orang lain yang mungkin menghadapi kekeliruan yang sama.

Selain itu, saya telah menyusun projek tunjuk cara dengan unit yang berkaitan dan ujian E2E selesai, yang mungkin menarik minat. Kod telah dimuat naik ke Github: https://github.com/woai3c/nestjs-demo.

Perbezaan Antara Ujian Unit dan Ujian E2E

Ujian unit dan ujian E2E ialah kaedah ujian perisian, tetapi ia mempunyai matlamat dan skop yang berbeza.

Ujian unit melibatkan pemeriksaan dan pengesahan unit terkecil yang boleh diuji dalam perisian. Fungsi atau kaedah, sebagai contoh, boleh dianggap sebagai unit. Dalam ujian unit, anda menyediakan output yang dijangkakan untuk pelbagai input fungsi dan mengesahkan ketepatan operasinya. Matlamat ujian unit adalah untuk mengenal pasti pepijat dalam fungsi dengan cepat dan ia mudah untuk ditulis dan dilaksanakan dengan cepat.

Sebaliknya, ujian E2E sering mensimulasikan senario pengguna dunia sebenar untuk menguji keseluruhan aplikasi. Sebagai contoh, bahagian hadapan biasanya menggunakan penyemak imbas atau penyemak imbas tanpa kepala untuk ujian, manakala bahagian belakang melakukannya dengan mensimulasikan panggilan API.

Dalam projek NestJS, ujian unit mungkin menilai perkhidmatan tertentu atau kaedah pengawal, seperti mengesahkan sama ada kaedah kemas kini dalam modul Pengguna mengemas kini pengguna dengan betul. Ujian E2E, walau bagaimanapun, mungkin meneliti perjalanan pengguna yang lengkap, daripada mencipta pengguna baharu kepada mengemas kini kata laluan mereka dan akhirnya memadamkan pengguna, yang melibatkan berbilang perkhidmatan dan pengawal.

Ujian Unit Penulisan

Menulis ujian unit untuk fungsi utiliti atau kaedah yang tidak melibatkan antara muka adalah agak mudah. Anda hanya perlu mempertimbangkan pelbagai input dan tulis kod ujian yang sepadan. Walau bagaimanapun, keadaan menjadi lebih kompleks apabila antara muka mula dimainkan. Mari gunakan kod sebagai contoh:

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;
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Kod di atas ialah kaedah validateUser dalam fail auth.service.ts, terutamanya digunakan untuk mengesahkan sama ada nama pengguna dan kata laluan yang dimasukkan oleh pengguna semasa log masuk adalah betul. Ia mengandungi logik berikut:

  1. Semak sama ada pengguna wujud berdasarkan nama pengguna; jika tidak, buang pengecualian 401 (pengecualian 404 juga boleh dilaksanakan).
  2. Lihat jika pengguna terkunci; jika ya, buang pengecualian 401 dengan mesej yang berkaitan.
  3. Sulitkan kata laluan dan bandingkan dengan kata laluan dalam pangkalan data; jika ia tidak betul, buang pengecualian 401 (tiga percubaan log masuk gagal berturut-turut akan mengunci akaun selama 5 minit).
  4. Jika log masuk berjaya, kosongkan kiraan percubaan log masuk yang gagal sebelum ini (jika berkenaan) dan kembalikan id pengguna dan nama pengguna ke peringkat seterusnya.

Seperti yang dapat dilihat, kaedah validateUser merangkumi empat logik pemprosesan dan kami perlu menulis kod ujian unit yang sepadan untuk empat mata ini untuk memastikan keseluruhan fungsi validateUser beroperasi dengan betul.

Kes Ujian Pertama

Apabila kami mula menulis ujian unit, kami menghadapi masalah: kaedah findOne perlu berinteraksi dengan pangkalan data dan ia mencari pengguna yang sepadan dalam pangkalan data melalui nama pengguna. Walau bagaimanapun, jika setiap ujian unit perlu berinteraksi dengan pangkalan data, ujian akan menjadi sangat rumit. Oleh itu, kita boleh mengejek data palsu untuk mencapai matlamat ini.

Sebagai contoh, anggap kami telah mendaftarkan pengguna bernama woai3c. Kemudian, semasa log masuk, data pengguna boleh diambil dalam kaedah validateUser melalui const entity = await this.usersService.findOne({ username });. Selagi baris kod ini boleh mengembalikan data yang dikehendaki, tiada masalah, walaupun tanpa interaksi pangkalan data. Kita boleh mencapai ini melalui data palsu. Sekarang, mari lihat kod ujian yang berkaitan untuk kaedah validateUser:

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;
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Kami mendapat data pengguna dengan memanggil kaedah findOne usersService, jadi kami perlu mengejek kaedah findOne usersService dalam kod ujian:

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...
  });
});
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Kami menggunakan jest.fn() untuk mengembalikan fungsi bagi menggantikan usersService.findOne() sebenar. Jika usersService.findOne() dipanggil sekarang, tidak akan ada nilai pulangan, jadi kes ujian unit pertama akan lulus:

beforeEach(async () => {
    usersService = {
      findOne: jest.fn(), // mock findOne method
    };
    const module = await Test.createTestingModule({
      providers: [        AuthService, // real AuthService, because we are testing its methods
        {
          provide: UsersService, // use mock usersService instead of real usersService
          useValue: usersService,
        },
      ],
    }).compile();
    authService = module.get<AuthService>(AuthService);
  });
Salin selepas log masuk
Salin selepas log masuk

Memandangkan findOne dalam entiti const = tunggu this.usersService.findOne({ nama pengguna }); daripada kaedah validateUser ialah fungsi palsu yang dipermainkan tanpa nilai pulangan, baris ke-2 hingga ke-4 kod dalam kaedah validateUser boleh dilaksanakan:

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

Lemparkan ralat 401, seperti yang dijangkakan.

Kes Ujian Kedua

Logik kedua dalam kaedah validateUser adalah untuk menentukan sama ada pengguna dikunci, dengan kod yang sepadan seperti berikut:

if (!entity) {
  throw new UnauthorizedException('User not found');
}
Salin selepas log masuk

Seperti yang anda lihat, kami boleh menentukan bahawa akaun semasa dikunci jika terdapat kunci masa kunciSehingga dalam data pengguna dan masa tamat kunci lebih besar daripada masa semasa. Oleh itu, kita perlu mengejek data pengguna dengan medan lockUntil:

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;
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Dalam kod ujian di atas, objek dikunciUser pertama kali ditakrifkan, yang mengandungi medan lockSehingga yang kami perlukan. Kemudian, ia digunakan sebagai nilai pulangan untuk findOne, dicapai oleh usersService.findOne.mockResolvedValueOnce(lockedUser);. Oleh itu, apabila kaedah validateUser dilaksanakan, data pengguna di dalamnya adalah data olok-olok, berjaya membenarkan kes ujian kedua lulus.

Liputan Ujian Unit

Liputan ujian unit (Liputan Kod) ialah metrik yang digunakan untuk menerangkan jumlah kod aplikasi yang telah dilindungi atau diuji oleh ujian unit. Ia biasanya dinyatakan sebagai peratusan, menunjukkan jumlah semua laluan kod yang mungkin telah diliputi oleh kes ujian.

Liputan ujian unit biasanya termasuk jenis berikut:

  • Liputan Talian: Berapa banyak baris kod yang diliputi oleh ujian.
  • Liputan Fungsi: Berapa banyak fungsi atau kaedah yang diliputi oleh ujian.
  • Liputan Cawangan: Berapa banyak cawangan kod yang diliputi oleh ujian (contohnya, pernyataan if/else).
  • Liputan Pernyataan: Berapa banyak pernyataan dalam kod yang diliputi oleh ujian.

Liputan ujian unit ialah metrik penting untuk mengukur kualiti ujian unit, tetapi ia bukan satu-satunya metrik. Kadar liputan yang tinggi boleh membantu untuk mengesan ralat dalam kod, tetapi ia tidak menjamin kualiti kod. Kadar liputan yang rendah mungkin bermakna terdapat kod yang belum diuji, berkemungkinan dengan ralat yang tidak dapat dikesan.

Imej di bawah menunjukkan keputusan liputan ujian unit untuk projek tunjuk cara:

How to write unit tests and Etests for NestJS applications

Untuk fail seperti perkhidmatan dan pengawal, secara amnya lebih baik untuk mempunyai liputan ujian unit yang lebih tinggi, manakala untuk fail seperti modul tidak perlu menulis ujian unit, dan tidak mungkin untuk menulisnya, kerana ia tidak bermakna. Imej di atas mewakili metrik keseluruhan untuk keseluruhan liputan ujian unit. Jika anda ingin melihat liputan ujian untuk fungsi tertentu, anda boleh membuka fail coverage/lcov-report/index.html dalam direktori akar projek. Sebagai contoh, saya ingin melihat situasi ujian khusus untuk kaedah validateUser:

How to write unit tests and Etests for NestJS applications

Seperti yang dapat dilihat, liputan ujian unit asal untuk kaedah validateUser bukanlah 100%, dan masih terdapat dua baris kod yang tidak dilaksanakan. Walau bagaimanapun, ia tidak begitu penting, kerana ia tidak menjejaskan empat nod pemprosesan utama dan seseorang itu tidak seharusnya mengejar liputan ujian tinggi secara unidimensi.

Menulis Ujian E2E

Dalam ujian unit, kami menunjukkan cara menulis ujian unit untuk setiap ciri fungsi validateUser(), menggunakan data yang dipermainkan untuk memastikan setiap ciri boleh diuji. Dalam ujian e2E, kita perlu mensimulasikan senario pengguna sebenar, jadi penyambungan ke pangkalan data untuk ujian adalah perlu. Oleh itu, kaedah dalam modul auth.service.ts yang akan kami uji semuanya berinteraksi dengan pangkalan data.

Modul pengesahan terutamanya termasuk ciri berikut:

  • Pendaftaran
  • Log masuk
  • Penyegaran token
  • Membaca maklumat pengguna
  • Menukar kata laluan
  • Memadam pengguna

Ujian E2E perlu menguji enam ciri ini satu demi satu, bermula dengan pendaftaran dan berakhir dengan pemadaman pengguna. Semasa ujian, kami boleh mencipta pengguna ujian yang berdedikasi untuk menjalankan ujian dan kemudian memadamkan pengguna ujian ini setelah selesai, supaya tidak meninggalkan sebarang maklumat yang tidak diperlukan dalam pangkalan data ujian.

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;
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Fungsi cangkuk beforeAll dijalankan sebelum semua ujian bermula, jadi kami boleh mendaftar akaun ujian TEST_USER_NAME di sini. Fungsi cangkuk afterAll berjalan selepas semua ujian tamat, jadi adalah sesuai untuk memadamkan akaun ujian TEST_USER_NAME di sini dan ia juga menguji fungsi pendaftaran dan pemadaman dengan mudah.

Dalam ujian unit bahagian sebelumnya, kami menulis ujian unit yang berkaitan di sekitar kaedah validateUser. Sebenarnya, kaedah ini dilaksanakan semasa log masuk untuk mengesahkan sama ada akaun dan kata laluan pengguna adalah betul. Oleh itu, ujian e2E ini juga akan menggunakan proses log masuk untuk menunjukkan cara mengarang kes ujian e2E.

Keseluruhan proses ujian log masuk termasuk lima ujian kecil:

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...
  });
});
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Lima ujian ini adalah seperti berikut:

  1. Log masuk berjaya, kembalikan 200
  2. Jika pengguna tidak wujud, buang pengecualian 401
  3. Jika kata laluan atau nama pengguna tidak diberikan, buang 400 pengecualian
  4. Log masuk dengan kata laluan yang salah, buang pengecualian 401
  5. Jika akaun dikunci, buang pengecualian 401

Sekarang mari mula menulis ujian e2E:

beforeEach(async () => {
    usersService = {
      findOne: jest.fn(), // mock findOne method
    };
    const module = await Test.createTestingModule({
      providers: [        AuthService, // real AuthService, because we are testing its methods
        {
          provide: UsersService, // use mock usersService instead of real usersService
          useValue: usersService,
        },
      ],
    }).compile();
    authService = module.get<AuthService>(AuthService);
  });
Salin selepas log masuk
Salin selepas log masuk

Menulis kod ujian e2E agak mudah: anda hanya memanggil antara muka dan kemudian mengesahkan hasilnya. Contohnya, untuk ujian log masuk yang berjaya, kami hanya perlu mengesahkan bahawa hasil yang dikembalikan ialah 200.

Empat ujian pertama agak mudah. Sekarang mari kita lihat ujian e2E yang lebih rumit, iaitu untuk mengesahkan sama ada akaun dikunci.

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;
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Apabila pengguna gagal log masuk tiga kali berturut-turut, akaun akan dikunci. Oleh itu, dalam ujian ini, kami tidak boleh menggunakan akaun ujian TEST_USER_NAME, kerana jika ujian berjaya, akaun ini akan dikunci dan tidak dapat meneruskan ujian berikut. Kami perlu mendaftarkan pengguna baharu TEST_USER_NAME2 yang lain secara khusus untuk menguji penguncian akaun dan memadamkan pengguna ini selepas ujian berjaya. Jadi, seperti yang anda boleh lihat, kod untuk ujian e2E ini agak besar, memerlukan banyak persediaan dan kerja pembongkaran, tetapi kod ujian sebenar hanyalah beberapa baris berikut:

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...
  });
});
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Menulis kod ujian e2E agak mudah. Anda tidak perlu mempertimbangkan data palsu atau liputan ujian. Ia memadai jika keseluruhan proses sistem berjalan seperti yang diharapkan.

Sama ada Menulis Ujian

Jika boleh, saya biasanya mengesyorkan ujian menulis. Melakukannya boleh meningkatkan keteguhan, kebolehselenggaraan dan kecekapan pembangunan sistem.

Meningkatkan Kekukuhan Sistem

Apabila menulis kod, kami biasanya menumpukan pada aliran program di bawah input biasa untuk memastikan fungsi teras berfungsi dengan baik. Walau bagaimanapun, kita mungkin sering terlepas pandang beberapa kes kelebihan, seperti input yang tidak normal. Ujian penulisan mengubah ini; ia memaksa anda untuk mempertimbangkan cara mengendalikan kes ini dan bertindak balas dengan sewajarnya, sekali gus mengelakkan ranap sistem. Boleh dikatakan ujian penulisan secara tidak langsung meningkatkan kekukuhan sistem.

Meningkatkan Kebolehselenggaraan

Mengambil alih projek baharu yang merangkumi ujian komprehensif boleh menjadi sangat menyenangkan. Mereka bertindak sebagai panduan, membantu anda memahami pelbagai fungsi dengan cepat. Hanya dengan melihat kod ujian, anda boleh memahami dengan mudah tingkah laku yang dijangkakan dan syarat sempadan setiap fungsi tanpa perlu melalui setiap baris kod fungsi tersebut.

Meningkatkan Kecekapan Pembangunan

Bayangkan, projek yang sudah lama tidak dikemas kini tiba-tiba menerima keperluan baharu. Selepas membuat perubahan, anda mungkin bimbang tentang memperkenalkan pepijat. Tanpa ujian, anda perlu menguji keseluruhan projek secara manual sekali lagi — membuang masa dan menjadi tidak cekap. Dengan ujian lengkap, satu arahan boleh memberitahu anda sama ada perubahan kod telah memberi kesan kepada fungsi sedia ada. Walaupun terdapat ralat, ia dapat dikesan dan ditangani dengan cepat.

Bila Tidak Menulis Ujian?

Untuk projek jangka pendek dan projek dengan lelaran keperluan yang sangat pantas, tidak disyorkan untuk menulis ujian. Contohnya, beberapa projek yang dimaksudkan untuk acara yang tidak berguna selepas acara tamat tidak memerlukan ujian. Selain itu, untuk projek yang menjalani lelaran keperluan yang sangat pantas, saya mengatakan bahawa ujian penulisan boleh meningkatkan kecekapan pembangunan, tetapi itu berdasarkan premis bahawa lelaran fungsi adalah perlahan. Jika fungsi yang baru anda selesaikan berubah dalam satu atau dua hari, kod ujian yang berkaitan mesti ditulis semula. Jadi, adalah lebih baik untuk tidak menulis ujian sama sekali dan bergantung pada pasukan ujian kerana ujian menulis sangat memakan masa dan tidak berbaloi dengan usaha.

Kesimpulan

Selepas menerangkan secara terperinci cara menulis ujian unit dan ujian e2E untuk projek NestJS, saya masih ingin mengulangi kepentingan ujian. Ia boleh meningkatkan keteguhan, kebolehselenggaraan, dan kecekapan pembangunan sistem. Jika anda tidak mempunyai peluang untuk menulis ujian, saya cadangkan anda memulakan projek latihan sendiri atau mengambil bahagian dalam beberapa projek sumber terbuka dan menyumbang kod kepada mereka. Projek sumber terbuka biasanya mempunyai keperluan kod yang lebih ketat. Kod penyumbang mungkin memerlukan anda menulis kes ujian baharu atau mengubah suai yang sedia ada.

Bahan Rujukan

  • NestJS: Rangka kerja untuk membina aplikasi bahagian pelayan Node.js yang cekap dan berskala.
  • MongoDB: Pangkalan data NoSQL yang digunakan untuk penyimpanan data.
  • Jest: Rangka kerja ujian untuk JavaScript dan TypeScript.
  • Supertest: Pustaka untuk menguji pelayan HTTP.

Atas ialah kandungan terperinci Cara menulis ujian unit dan Etest untuk aplikasi NestJS. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

sumber:dev.to
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Artikel terbaru oleh pengarang
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan