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; } </useraccountdto>
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:
- Semak sama ada pengguna wujud berdasarkan nama pengguna; jika tidak, buang pengecualian 401 (pengecualian 404 juga boleh dilaksanakan).
- Lihat jika pengguna terkunci; jika ya, buang pengecualian 401 dengan mesej yang berkaitan.
- 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).
- 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; } </useraccountdto>
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 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... }); }); </authservice></record>
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); }); </authservice>
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); });
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'); }
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; } </useraccountdto>
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:
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:
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; } </useraccountdto>
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 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... }); }); </authservice></record>
Lima ujian ini adalah seperti berikut:
- Log masuk berjaya, kembalikan 200
- Jika pengguna tidak wujud, buang pengecualian 401
- Jika kata laluan atau nama pengguna tidak diberikan, buang 400 pengecualian
- Log masuk dengan kata laluan yang salah, buang pengecualian 401
- 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); }); </authservice>
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; } </useraccountdto>
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 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... }); }); </authservice></record>
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!

Saya membina aplikasi SaaS multi-penyewa berfungsi (aplikasi edTech) dengan alat teknologi harian anda dan anda boleh melakukan perkara yang sama. Pertama, apakah aplikasi SaaS multi-penyewa? Aplikasi SaaS Multi-penyewa membolehkan anda melayani beberapa pelanggan dari Sing

Artikel ini menunjukkan integrasi frontend dengan backend yang dijamin oleh permit, membina aplikasi edtech SaaS yang berfungsi menggunakan Next.Js. Frontend mengambil kebenaran pengguna untuk mengawal penglihatan UI dan memastikan permintaan API mematuhi dasar peranan

JavaScript adalah bahasa utama pembangunan web moden dan digunakan secara meluas untuk kepelbagaian dan fleksibiliti. 1) Pembangunan front-end: Membina laman web dinamik dan aplikasi satu halaman melalui operasi DOM dan kerangka moden (seperti React, Vue.js, sudut). 2) Pembangunan sisi pelayan: Node.js menggunakan model I/O yang tidak menyekat untuk mengendalikan aplikasi konkurensi tinggi dan masa nyata. 3) Pembangunan aplikasi mudah alih dan desktop: Pembangunan silang platform direalisasikan melalui reaktnatif dan elektron untuk meningkatkan kecekapan pembangunan.

Trend terkini dalam JavaScript termasuk kebangkitan TypeScript, populariti kerangka dan perpustakaan moden, dan penerapan webassembly. Prospek masa depan meliputi sistem jenis yang lebih berkuasa, pembangunan JavaScript, pengembangan kecerdasan buatan dan pembelajaran mesin, dan potensi pengkomputeran IoT dan kelebihan.

JavaScript adalah asas kepada pembangunan web moden, dan fungsi utamanya termasuk pengaturcaraan yang didorong oleh peristiwa, penjanaan kandungan dinamik dan pengaturcaraan tak segerak. 1) Pengaturcaraan yang didorong oleh peristiwa membolehkan laman web berubah secara dinamik mengikut operasi pengguna. 2) Penjanaan kandungan dinamik membolehkan kandungan halaman diselaraskan mengikut syarat. 3) Pengaturcaraan Asynchronous memastikan bahawa antara muka pengguna tidak disekat. JavaScript digunakan secara meluas dalam interaksi web, aplikasi satu halaman dan pembangunan sisi pelayan, sangat meningkatkan fleksibiliti pengalaman pengguna dan pembangunan silang platform.

Python lebih sesuai untuk sains data dan pembelajaran mesin, manakala JavaScript lebih sesuai untuk pembangunan front-end dan penuh. 1. Python terkenal dengan sintaks ringkas dan ekosistem perpustakaan yang kaya, dan sesuai untuk analisis data dan pembangunan web. 2. JavaScript adalah teras pembangunan front-end. Node.js menyokong pengaturcaraan sisi pelayan dan sesuai untuk pembangunan stack penuh.

JavaScript tidak memerlukan pemasangan kerana ia sudah dibina dalam pelayar moden. Anda hanya memerlukan editor teks dan penyemak imbas untuk memulakan. 1) Dalam persekitaran penyemak imbas, jalankan dengan memasukkan fail HTML melalui tag. 2) Dalam persekitaran Node.js, selepas memuat turun dan memasang node.js, jalankan fail JavaScript melalui baris arahan.

Cara Menghantar Pemberitahuan Tugas di Quartz terlebih dahulu Apabila menggunakan pemasa kuarza untuk menjadualkan tugas, masa pelaksanaan tugas ditetapkan oleh ekspresi cron. Sekarang ...


Alat AI Hot

Undresser.AI Undress
Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover
Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool
Gambar buka pakaian secara percuma

Clothoff.io
Penyingkiran pakaian AI

AI Hentai Generator
Menjana ai hentai secara percuma.

Artikel Panas

Alat panas

Muat turun versi mac editor Atom
Editor sumber terbuka yang paling popular

mPDF
mPDF ialah perpustakaan PHP yang boleh menjana fail PDF daripada HTML yang dikodkan UTF-8. Pengarang asal, Ian Back, menulis mPDF untuk mengeluarkan fail PDF "dengan cepat" dari tapak webnya dan mengendalikan bahasa yang berbeza. Ia lebih perlahan dan menghasilkan fail yang lebih besar apabila menggunakan fon Unicode daripada skrip asal seperti HTML2FPDF, tetapi menyokong gaya CSS dsb. dan mempunyai banyak peningkatan. Menyokong hampir semua bahasa, termasuk RTL (Arab dan Ibrani) dan CJK (Cina, Jepun dan Korea). Menyokong elemen peringkat blok bersarang (seperti P, DIV),

MantisBT
Mantis ialah alat pengesan kecacatan berasaskan web yang mudah digunakan yang direka untuk membantu dalam pengesanan kecacatan produk. Ia memerlukan PHP, MySQL dan pelayan web. Lihat perkhidmatan demo dan pengehosan kami.

Dreamweaver Mac版
Alat pembangunan web visual

Notepad++7.3.1
Editor kod yang mudah digunakan dan percuma