Kuliah Pengujian Unit JS - Panduan Praktikal
Ini adalah ringkasan kursus "Pengujian Unit JavaScript - Panduan Praktikal" yang disyarah oleh Maximilian Schwarzmüller.
pengenalan
Menyelam ke dalam ujian automatik boleh menjadi idea yang bagus kerana berbanding dengan ujian manual, ia lebih mudah diramal dan konsisten.
Mari kita pertimbangkan senario di mana anda menambah ciri baharu atau menukar sebahagian daripada kod anda.
Anda tidak semestinya mengetahui atau menyedari setiap bahagian atau semua bahagian kod yang benar-benar terjejas.
Dengan ujian manual, kami perlu menguji (atau mencuba) keseluruhan aplikasi, atau sekurang-kurangnya, perkara yang mungkin terjejas oleh perubahan. kenapa? Kerana kita perlu melihat sama ada semuanya masih berfungsi dan perubahan kecil atau ciri baharu itu tidak memecahkan kod di suatu tempat.
Jadi, seperti yang kita boleh bayangkan, banyak kerja.
Selain itu, ia tidak boleh dijamin bahawa semua yang perlu diuji akan diuji, dan setiap kali sesuatu berubah, tidak dapat dipastikan bahawa perkara akan diuji dengan cara yang sama.
Dengan ujian automatik, kami mempunyai usaha awal, tetapi ia membawa banyak faedah kemudian.
Daripada pelbagai ujian automatik yang kami ada pada masa kini, di sini kami akan bercakap tentang ujian unit dan penyepaduan, tetapi terutamanya mengenai ujian unit.
Mari kita fikirkan tentang unit sebagai bahagian terkecil kod kita, contohnya, fungsi atau kelas. Jadi, dalam ujian unit, seperti namanya, kami akan menguji setiap unit aplikasi kami. Jadi, jika kita mempunyai, sebagai contoh, 50 fungsi, kita akan membuat ujian untuk kesemuanya atau majoriti. Ini kerana kami ingin menjamin bahawa setiap bahagian, setiap unit, berfungsi sebagaimana mestinya, seperti yang pertama kali dimaksudkan.
Ujian integrasi, sebaliknya, akan mengambil berat tentang menguji unit ini bersama-sama, atau, lebih baik dikatakan, cara ia berfungsi bersama dan jika ia berfungsi dengan baik bersama. Itu kerana walaupun kita menguji unit sahaja, itu bukan jaminan bahawa mereka bekerja bersama atau bekerja sebagaimana mestinya.
Pembangunan Dipacu Ujian (TDD)
Kita juga harus sedar tentang TDD, bentuk pendek Pembangunan Dipacu Ujian.
TDD ialah rangka kerja/falsafah yang membawa kita berfikir tentang menulis ujian yang gagal dahulu dan kemudian melaksanakan kod yang akan menjadikan ujian itu berjaya. Dan kemudian refactor, sebagai perkara kitaran.
Peringatan
Pastikan ujian anda mudah!
Apabila orang lain perlu membaca kod anda atau malah anda dalam senario masa hadapan, adalah penting untuk tidak mengambil masa terlalu lama untuk memahaminya. Ia perlu menjadi tugas yang mudah.
Vitest
Untuk panduan ini, kami akan bekerjasama dengan Vitest, alat berdasarkan apl Jest yang terkenal untuk ujian dan banyak lagi. Di sini, ia tidak bertujuan untuk mendalami sintaks Vitest atau semua fungsi, tetapi sebagai alat untuk memahami teras ujian.
Jika anda ingin mempelajari atau melihat semua yang Vitest boleh membantu kami, pergi ke dokumentasi di pautan berikut: Dokumentasi Vitest
Petunjuk
Vitest boleh berfungsi sama seperti alat Webpack untuk penggabungan, jadi apabila menggunakan notasi Modul ES, kami sebenarnya tidak perlu memaklumkan secara jelas sambungan fail yang kami import. Contohnya:
Import matematik daripada './math/Math'
Yang sebaliknya ialah:
Import matematik daripada './math/Math.js'
Amalan yang baik
Berikut ialah panduan kecil untuk membawa anda ke dalam amalan yang baik dalam rutin ujian penulisan anda.
Menulis ujian yang baik
Ujian unit dan penyepaduan boleh menjadi sangat berguna, tetapi hanya jika ditulis dengan baik. Dan untuk itu, kita boleh mengikuti satu siri "amalan baik" yang akan diterokai di bawah:
Hanya uji kod anda
Apabila kita bercakap tentang menguji kod kita sendiri, ini bermakna kod pihak ketiga tidak bertanggungjawab untuk kita menguji. Nah, ini bertujuan untuk mereka yang menulis kod pihak ketiga untuk memastikan ia berfungsi dengan baik dan baik, jadi menjadi tanggungjawab mereka untuk mengujinya.
Juga kerana anda tidak akan dapat mengubahnya, jadi tidak berguna untuk mengujinya.
Mengetahui cara membezakan ujian
Anda tidak akan menguji kod sebelah pelayan anda secara tersirat melalui kod sebelah pelanggan anda.
Dan anda akan menguji reaksi pihak pelanggan anda terhadap respons dan ralat yang berbeza.
Jadi asingkan ujian anda, ujian untuk pembangunan bahagian hadapan anda dan ujian untuk pembangunan bahagian belakang anda.
AAA
- Susun: tentukan persekitaran dan nilai ujian
- Akta: jalankan kod/fungsi sebenar yang perlu diuji
- Tegaskan: nilaikan nilai/hasil yang dihasilkan dan bandingkan dengan nilai/hasil yang dijangkakan.
Di sini terdapat contoh kod Vitest:
import { expect, it } from 'vitest'; import { toSum } from './math'; it('should sum two numbers', () => { //Arrange const num1 = 1; const num2 = 2; //Act const result = toSum(num1, num2); //Assert const expectedResult = num1 + num2; expect(result).toBe(expectedResult); });
Hanya untuk menjelaskan perkara yang dilakukan oleh kod tersebut, sekiranya anda tidak menyedarinya.
Pertama sekali, kami mengimport "expect" dan "it" daripada vitest, iaitu fungsi yang kami perlukan dan fungsi "toSum", iaitu fungsi yang dibina untuk contoh tetapi berada dalam fail lain.
The "it" works as the scope for our test; it receives a string that behaves as the identifier and a function that will run the test code. Here is very simple; we are saying that it should sum two numbers, that's our expectation for the function and for the test.
In the arrange part we create the variables which will be passed to the "toSum" function. Then, in the act part, we create a constant that will receive the result of the function. Finally, in the assert, we will store the expected result, which would be the sum of the two numbers and use "expect" from Vitest to make the assertion. Basically, we are saying that we expect the result to be the same as the expected result.
There are many assertion possibilities, so please do check the documentation for further study.
Note: "it" is an alias for "test"
Also, it's very important the following line:
const expectedResult = num1 + num2;
Imagine if we've done it like this:
const expectedResult = 3;
It's okay for the current test because we are indeed expecting 3 as the result.
But, imagine in the future, someone changes "num1" or "num2", and forgets to change the "expectedResult"; it would not work if the result of the sum was not 3.
Essence of what is being tested
If you, for example, created a function that is going to receive an array of numbers as an argument and you need to test if it actually received an array of numbers in your test file, you just need to reference an array, for example:
const items = [1, 2];
You don't need to create a bigger array, for example:
const items = [1, 2, 3, 4, 5, 6, 7, 8];
It's unnecessary and redundant. Keep it short, simple and concise, so you will make sure that you are only testing what needs to be tested or what is important for the function.
Test one thing
You can think of one thing as one feature or one behavior. For example, if you have a function that will sum two numbers (the same example above) and you need to make sure that is summing two numbers (because indeed that's the main functionality) but also that the output is of type number, then, you can separate it into two assertions, for example:
import { describe, expect, it } from 'vitest'; import { toSum } from './math'; describe('toSum()', () => { it('should sum two numbers', () => { const num1 = 1; const num2 = 2; const result = toSum(num1, num2); const expectedResult = num1 + num2; expect(result).toBe(expectedResult); }); it('should output a result of type number', () => { const num1 = 1; const num2 = 2; const result = toSum(num1, num2); const expectedResult = num1 + num2; expect(result).toBe(expectedResult); }); })
If you're wondering what describe does, it help us to create suites. As many suites as we want, like dividing blocks of tests. It keeps our code organized, clear, and easier to understand the outputting.
Here's an example using the toSum function:
As you can see in the image above, it will show us the file path, and after that the "describe" name, and then the "it" name. It's a good idea to keep the describer name short and referencing the function to what the tests are about.
And you could have describers inside describers to organize even further, it's up to you.
SO,
when we create our tests following good practices, we are creating tests that will actually help us on what's needed to be tested. And also, testing forces us to write better code. For that, we can write good functions that will hold only the logic of that function so it'll be easier to test what's need to be tested.
Code coverage
It's important to understand also that coverage doesn't mean good testing or testing that is useful and meaningful for the application. Well, you could cover 100% of your code with meaningless tests after all, or, missing important tests that you didn't think of.
Don't see a high amount of code coverage as the ultimate goal!
You will want to try and test cover the majority of the units (functions or classes) in your application, because that's what unit testing is about, but, there is some code that doesn't need to be tested.
Vitest comes with a built-in functionality to help us measure the code coverage; you can access in the following link: Vitest coverage tool
Callbacks and Async Functions
As callbacks and async functions exhibit specific behavior in Vitest, this section is dedicated to exploring them superficially.
When testing for a callback, keep in mind that Vitest does not wait for the response or for the callback to be executed. Therefore, we need to use the "done" argument.
Consider the following test as an example:
import { expect, it } from 'vitest'; import { generateToken } from './async-example'; it('should generate a token value', (done) => { const email = 'test@mail.com'; generateToken(email, (err, token) => { expect(token).toBeDefined(); done() }) })
Now, we are working with a callback function. Notice that there's a parameter being passed. The "done".
Vitest now needs to wait until the done function is called.
What would happen if we didn't use the "done" argument? The "expect" wouldn't be executed.
Try and catch
Still in that function, imagine if we changed toBeDefined to toBe, as in the image below:
import { expect, it } from 'vitest'; import { generateToken } from './async-example'; it('should generate a token value', (done) => { const email = 'test@mail.com'; generateToken(email, (err, token) => { expect(token).toBe(2); done(); }); })
By default, in Vitest, the "toBe" function throws an error each time something doesn't go as expected, in this case, if the token returned wasn't 2.
However, as we are working with a callback, we will need to add an exception handling syntax, such as "try and catch", because if we don't do so, the test will timeout.
import { expect, it } from 'vitest'; import { generateToken } from './async-example'; it('should generate a token value', (done) => { const email = 'test@mail.com'; try { generateToken(email, (err, token) => { expect(token).toBe(2); }); } catch (error) { done(error); } })
Since we are dealing with an error, we also need to pass this error to the "done" function.
Promises
Now, when working with promises, it's a bit easier, or should we say, simpler.
import { expect, it } from 'vitest'; import { generateTokenPromise } from './async-example'; it('should generate a token value', () => { const email = 'test@mail.com'; return expect(generateTokenPromise(email)).resolves.toBeDefined(); //or return expect(generateTokenPromise(email)).rejects.toBe(); });
Here we have two possibilities: resolves and rejects
The "return" statement guarantees Vitest waits for the promise to be resolved.
Alternatively, we have:
import { expect, it } from 'vitest'; import { generateTokenPromise } from './async-example'; it('should generate a token value', async () => { const email = 'test@mail.com'; const token = await generateTokenPromise(email); expect(token).resolves.toBeDefined(); // or expect(token).rejects.toBe(); })
Here we don't need to use "return" because "async/await" is being used (since a function annotated with "async" returns a promise implicitly).
Hooks, spies and mocks
Here, we are going to explore a little bit of these important functionalities that Vitest provides to us.
Hooks
Imagine working with a bunch of tests that use the same variable, and you don't want to initialize it every single time in every single test because it's the same.
Hooks can help us in this case because you can use functions provided by it that allow us to reuse this variable.
Functions available: "beforeAll", "beforeEach", "afterEach", "afterAll".
Here goes a simple example just to show how it works:
import { beforeEach } from 'vitest'; let myVariable; beforeEach(() => { myVariable = ""; }); it('sentence', () => { myVariable = "Hello"; }); it('another sentence', () => { myVariable += 2; });
Now, imagine the same but without the hook:
let myVariable; it('sentence', () => { myVariable = "Hello"; }); it('another sentence', () => { myVariable += 2; });
As we can see, when using "beforeEach" and a global variable, before each test starts to execute, the variable will be "cleaned". This allows the tests to use the variable as if it were fresh.
But, without using the hook and using a global variable, in some cases, things would be tricky. In the example of the test "another sentence," if we didn't clean the variable, it would be holding "Hello" because the "sentence" test is run first. And that's not what we want.
Mocks and Spies
Mocks and spies are mainly to handle side effects and external dependencies.
We could say that spies help us deal with side effects in our functions, and mocks help us deal with side effects of external dependencies.
For that, you will have to import "vi" from vitest.
To build a spy, you can use "vi.fn()" and for a mock "vi.mock()". Inside each function, you will pass the name to the other function (your or external).
So, spies and mocks kind of replace the actual functions with other functions or empty functions.
Mocks will be available only for tests of the file you called them and Vitest, behind the scenes, puts them at the start of the file.
Conclusion
In summary, you need to consider what the unit should or should not do. To achieve this, you can utilize the "it" syntax provided by Vitest, which takes a string describing your expectations and a function that will test the given expectations.
The name of the test should be short, simple and easy to understand.
The testing magic lies in thinking about aspects that were not initially considered, leading to code improvement. This process helps prevent errors and promotes a clearer understanding of what the function should do and its expected behaviors.
Atas ialah kandungan terperinci Kuliah Pengujian Unit JS - Panduan Praktikal. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

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

Video Face Swap
Tukar muka dalam mana-mana video dengan mudah menggunakan alat tukar muka AI percuma kami!

Artikel Panas

Alat panas

Notepad++7.3.1
Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina
Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1
Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6
Alat pembangunan web visual

SublimeText3 versi Mac
Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Topik panas











Python lebih sesuai untuk pemula, dengan lengkung pembelajaran yang lancar dan sintaks ringkas; JavaScript sesuai untuk pembangunan front-end, dengan lengkung pembelajaran yang curam dan sintaks yang fleksibel. 1. Sintaks Python adalah intuitif dan sesuai untuk sains data dan pembangunan back-end. 2. JavaScript adalah fleksibel dan digunakan secara meluas dalam pengaturcaraan depan dan pelayan.

Penggunaan utama JavaScript dalam pembangunan web termasuk interaksi klien, pengesahan bentuk dan komunikasi tak segerak. 1) kemas kini kandungan dinamik dan interaksi pengguna melalui operasi DOM; 2) pengesahan pelanggan dijalankan sebelum pengguna mengemukakan data untuk meningkatkan pengalaman pengguna; 3) Komunikasi yang tidak bersesuaian dengan pelayan dicapai melalui teknologi Ajax.

Aplikasi JavaScript di dunia nyata termasuk pembangunan depan dan back-end. 1) Memaparkan aplikasi front-end dengan membina aplikasi senarai TODO, yang melibatkan operasi DOM dan pemprosesan acara. 2) Membina Restfulapi melalui Node.js dan menyatakan untuk menunjukkan aplikasi back-end.

Memahami bagaimana enjin JavaScript berfungsi secara dalaman adalah penting kepada pemaju kerana ia membantu menulis kod yang lebih cekap dan memahami kesesakan prestasi dan strategi pengoptimuman. 1) aliran kerja enjin termasuk tiga peringkat: parsing, penyusun dan pelaksanaan; 2) Semasa proses pelaksanaan, enjin akan melakukan pengoptimuman dinamik, seperti cache dalam talian dan kelas tersembunyi; 3) Amalan terbaik termasuk mengelakkan pembolehubah global, mengoptimumkan gelung, menggunakan const dan membiarkan, dan mengelakkan penggunaan penutupan yang berlebihan.

Python dan JavaScript mempunyai kelebihan dan kekurangan mereka sendiri dari segi komuniti, perpustakaan dan sumber. 1) Komuniti Python mesra dan sesuai untuk pemula, tetapi sumber pembangunan depan tidak kaya dengan JavaScript. 2) Python berkuasa dalam bidang sains data dan perpustakaan pembelajaran mesin, sementara JavaScript lebih baik dalam perpustakaan pembangunan dan kerangka pembangunan depan. 3) Kedua -duanya mempunyai sumber pembelajaran yang kaya, tetapi Python sesuai untuk memulakan dengan dokumen rasmi, sementara JavaScript lebih baik dengan MDNWebDocs. Pilihan harus berdasarkan keperluan projek dan kepentingan peribadi.

Kedua -dua pilihan Python dan JavaScript dalam persekitaran pembangunan adalah penting. 1) Persekitaran pembangunan Python termasuk Pycharm, Jupyternotebook dan Anaconda, yang sesuai untuk sains data dan prototaip cepat. 2) Persekitaran pembangunan JavaScript termasuk node.js, vscode dan webpack, yang sesuai untuk pembangunan front-end dan back-end. Memilih alat yang betul mengikut keperluan projek dapat meningkatkan kecekapan pembangunan dan kadar kejayaan projek.

C dan C memainkan peranan penting dalam enjin JavaScript, terutamanya digunakan untuk melaksanakan jurubahasa dan penyusun JIT. 1) C digunakan untuk menghuraikan kod sumber JavaScript dan menghasilkan pokok sintaks abstrak. 2) C bertanggungjawab untuk menjana dan melaksanakan bytecode. 3) C melaksanakan pengkompil JIT, mengoptimumkan dan menyusun kod hot-spot semasa runtime, dan dengan ketara meningkatkan kecekapan pelaksanaan JavaScript.

Python lebih sesuai untuk sains data dan automasi, manakala JavaScript lebih sesuai untuk pembangunan front-end dan penuh. 1. Python berfungsi dengan baik dalam sains data dan pembelajaran mesin, menggunakan perpustakaan seperti numpy dan panda untuk pemprosesan data dan pemodelan. 2. Python adalah ringkas dan cekap dalam automasi dan skrip. 3. JavaScript sangat diperlukan dalam pembangunan front-end dan digunakan untuk membina laman web dinamik dan aplikasi satu halaman. 4. JavaScript memainkan peranan dalam pembangunan back-end melalui Node.js dan menyokong pembangunan stack penuh.
