Cara Memulakan Unit Menguji Kod JavaScript Anda

Kita semua tahu bahawa kita mesti menulis ujian unit. Tetapi, sukar untuk mengetahui di mana untuk memulakan dan berapa banyak masa yang perlu dicurahkan untuk ujian berbanding dengan pelaksanaan sebenar. Jadi, di mana untuk memulakan? Dan hanya mengenai kod ujian atau adakah ujian unit mempunyai faedah lain?

Dalam artikel ini, saya akan menerangkan pelbagai jenis ujian, dan manfaat ujian unit bagi pasukan pembangunan. Saya akan mempamerkan Jest - kerangka ujian JavaScript.

Jenis ujian yang berbeza

Sebelum kita mengetahui spesifik pengujian unit, saya ingin melakukan pelbagai jenis ujian dengan cepat. Selalunya terdapat kekeliruan di sekitar mereka dan saya tidak terkejut. Kadang-kadang garis di antara mereka agak tipis.

Ujian unit

Ujian unit hanya menguji satu bahagian pelaksanaan anda. Satu unit. Tidak ada pergantungan atau integrasi, tidak ada kerangka khusus. Mereka seperti kaedah yang mengembalikan pautan dalam bahasa tertentu:

export function getAboutUsLink(language){ switch (language.toLowerCase()){ case englishCode.toLowerCase(): return '/about-us'; case spanishCode.toLowerCase(): return '/acerca-de'; } return ''; }

Ujian integrasi

Pada satu ketika, kod anda berkomunikasi dengan pangkalan data, sistem fail atau pihak ketiga yang lain. Bahkan boleh menjadi modul lain dalam aplikasi anda.

Pelaksanaan itu harus diuji dengan ujian integrasi. Mereka biasanya mempunyai persediaan yang lebih rumit yang melibatkan penyediaan persekitaran ujian, memulakan pergantungan, dan sebagainya.

Ujian fungsional

Ujian unit dan ujian integrasi memberi anda keyakinan bahawa aplikasi anda berfungsi. Ujian fungsional melihat aplikasi dari sudut pandangan pengguna dan menguji bahawa sistem berfungsi seperti yang diharapkan.

Dalam rajah di atas, anda melihat bahawa ujian unit membentuk pangkalan besar rangkaian ujian aplikasi anda. Biasanya, mereka kecil, ada banyak, dan ia dilaksanakan secara automatik.

Jadi sekarang mari masuk ke ujian unit dengan lebih terperinci.

Mengapa Saya Perlu Bersusah payah Menulis Ujian Unit?

Setiap kali saya bertanya kepada pembangun sama ada mereka menulis ujian untuk aplikasi mereka, mereka selalu memberitahu saya: "Saya tidak mempunyai masa untuk mereka" atau "Saya tidak memerlukannya, saya tahu ia berfungsi."

Oleh itu, saya tersenyum dengan sopan dan memberitahu mereka apa yang ingin saya sampaikan kepada anda. Ujian unit bukan hanya mengenai ujian. Mereka juga membantu anda dengan cara lain, sehingga anda dapat:

Pastikan kod anda berfungsi. Bilakah kali terakhir anda melakukan perubahan kod, binaan anda gagal, dan separuh daripada aplikasi anda berhenti berfungsi? Milik saya minggu lalu.

Tetapi itu masih baik. Masalah sebenarnya ialah apabila binaan berjaya, perubahan diterapkan, dan aplikasi anda mula tidak stabil.

Apabila itu berlaku, anda mula hilang kepercayaan pada kod anda dan akhirnya berdoa agar aplikasi berfungsi. Ujian unit akan membantu anda menemui masalah lebih awal dan mendapat keyakinan.

Buat keputusan seni bina yang lebih baik. Kod berubah, tetapi beberapa keputusan mengenai platform, modul, struktur, dan lain-lain perlu dibuat semasa peringkat awal projek.

Apabila anda mula memikirkan pengujian unit tepat pada mulanya, ia akan membantu anda menyusun kod anda dengan lebih baik dan mencapai masalah yang betul. Anda tidak akan tergoda untuk memberikan banyak tanggungjawab kepada blok kod tunggal kerana itu akan menjadi mimpi buruk untuk ujian unit.

Tentukan fungsi sebelum pengekodan. Anda menulis tandatangan kaedah dan mula menerapkannya dengan segera. Oh, tetapi apa yang harus berlaku sekiranya parameter nol? Bagaimana jika nilainya berada di luar jangkauan yang diharapkan atau mengandungi terlalu banyak watak? Adakah anda membuang pengecualian atau mengembalikan nol?

Ujian unit akan membantu anda mengetahui semua kes ini. Lihat semula soalannya dan anda akan dapati bahawa inilah yang menentukan kes ujian unit anda.

Saya pasti ada banyak lagi faedah untuk menulis ujian unit. Inilah yang saya ingat dari pengalaman saya. Yang saya belajar dengan cara yang sukar.

Cara Menulis Ujian Unit JavaScript Pertama Anda

Tetapi mari kita kembali ke JavaScript. Kami akan memulakan dengan Jest, yang merupakan kerangka pengujian JavaScript. Ini adalah alat yang membolehkan pengujian unit automatik, memberikan liputan kod, dan memungkinkan kita mengejek objek dengan mudah. Jest juga mempunyai lanjutan untuk Visual Studio Code yang terdapat di sini.

Terdapat juga kerangka lain, jika anda berminat, anda boleh menyemaknya dalam artikel ini.

npm i jest --save-dev 

Mari gunakan kaedah yang disebutkan sebelumnya getAboutUsLinksebagai pelaksanaan yang ingin kami uji:

const englishCode = "en-US"; const spanishCode = "es-ES"; function getAboutUsLink(language){ switch (language.toLowerCase()){ case englishCode.toLowerCase(): return '/about-us'; case spanishCode.toLowerCase(): return '/acerca-de'; } return ''; } module.exports = getAboutUsLink; 

Saya memasukkannya ke dalam index.jsfail. Kita boleh menulis ujian dalam fail yang sama, tetapi amalan yang baik adalah memisahkan ujian unit ke dalam fail khusus.

Corak penamaan yang biasa merangkumi {filename}.test.jsdan {filename}.spec.js. Saya menggunakan yang pertama index.test.js,:

const getAboutUsLink = require("./index"); test("Returns about-us for english language", () => { expect(getAboutUsLink("en-US")).toBe("/about-us"); }); 

Pertama, kita perlu mengimport fungsi yang ingin kita uji. Setiap ujian didefinisikan sebagai pemanggilan testfungsi. Parameter pertama adalah nama ujian untuk rujukan anda. Yang lain adalah fungsi anak panah di mana kita memanggil fungsi yang ingin kita uji dan menentukan hasil yang kita harapkan. Saya

Dalam kes ini, kita memanggil getAboutUsLinkfungsi dengan en-USsebagai parameter bahasa. Kami mengharapkan hasilnya /about-us.

Sekarang kita boleh memasang Jest CLI secara global dan menjalankan ujian:

npm i jest-cli -g jest 

Sekiranya anda melihat ralat berkaitan konfigurasi, pastikan package.jsonfail anda ada. Sekiranya anda tidak, buat satu menggunakan npm init.

Anda mesti melihat sesuatu seperti ini:

 PASS ./index.test.js √ Returns about-us for english language (4ms) console.log index.js:15 /about-us Test Suites: 1 passed, 1 total Tests: 1 passed, 1 total Snapshots: 0 total Time: 2.389s 

Great job! This was the first simple JavaScript unit test from start to end. If you installed the Visual Studio Code extension, it will run tests automatically once you save a file. Let's try it by extending the test with this line:

expect(getAboutUsLink("cs-CZ")).toBe("/o-nas"); 

Once you save the file, Jest will inform you that the test failed. That helps you discover potential issues even before committing your changes.

Testing Advanced Functionality and Mocking Services

In real life, the language codes for the getAboutUsLink method would not be constants in the same file. Their value is typically used throughout the project so they would be defined in their own module and imported into all functions that use them.

import { englishCode, spanishCode } from './LanguageCodes' 

You can import these constants into the test the same way. But the situation will get more complicated if you're working with objects instead of simple constants. Take a look at this method:

import { UserStore } from './UserStore' function getUserDisplayName(){ const user = UserStore.getUser(userId); return `${user.LastName}, ${user.FirstName}`; } 

This method uses imported UserStore:

class User { getUser(userId){ // logic to get data from a database } setUser(user){ // logic to store data in a database } } let UserStore = new User(); export { UserStore } 

In order to properly unit test this method, we need to mock UserStore. A mock is a substitute for the original object. It allows us to separate dependencies and real data from the tested method's implementation just like dummies help with crash tests of cars instead of real people.

If we didn't use the mock, we'd be testing both this function and the store. That would be an integration test and we would likely need to mock the used database.

Mocking a Service

To mock objects, you can either provide a mocking function or a manual mock. I will focus on the latter as I have a plain and simple use-case. But feel free to check out other mocking possibilities Jest provides.

jest.mock('./UserStore', () => ({     UserStore: ({         getUser: jest.fn().mockImplementation(arg => ({             FirstName: 'Ondrej',             LastName: 'Polesny'         })), setUser: jest.fn()     }) })); 

First, we need to specify what are we mocking - the ./UserStore module. Next, we need to return the mock that contains all exported objects from that module.

In this sample, it's only the User object named UserStore with the function getUser. But with real implementations, the mock may be much longer. Any functions you don't really care about in the scope of unit testing can be easily mocked with jest.fn().

The unit test for the getUserDisplayName function is similar to the one we created before:

test("Returns display name", () => {     expect(getUserDisplayName(1)).toBe("Polesny, Ondrej"); }) 

As soon as I save the file, Jest tells me I have 2 passing tests. If you're executing tests manually, do so now and make sure you see the same result.

Code Coverage Report

Now that we know how to test JavaScript code, it's good to cover as much code as possible with tests. And that is hard to do. In the end, we're just people. We want to get our tasks done and unit tests usually yield an unwanted workload that we tend to overlook. Code coverage is a tool that helps us fight that.

Code coverage will tell you how big a portion of your code is covered by unit tests. Take for example my first unit test checking the getAboutUsLink function:

test("Returns about-us for english language", () => {    expect(getAboutUsLink("en-US")).toBe("/about-us"); }); 

It checks the English link, but the Spanish version stays untested. The code coverage is 50%. The other unit test is checking the getDisplayName function thoroughly and its code coverage is 100%. Together, the total code coverage is 67%. We had 3 use cases to test, but our tests only cover 2 of them.

To see the code coverage report, type the following command into the terminal:

jest --coverage 

Or, if you're using Visual Studio Code with the Jest extension, you can run the command (CTRL+SHIFT+P) Jest: Toggle Coverage Overlay. It will show you right in the implementation which lines of code are not covered with tests.

By running the coverage check, Jest will also create an HTML report. Find it in your project folder under coverage/lcov-report/index.html.

Now, I don't have to mention that you should strive for 100% code coverage, right? :-)

Summary

In this article, I showed you how to start with unit testing in JavaScript. While it's nice to have your code coverage shine at 100% in the report, in reality, it's not always possible to (meaningfully) get there. The goal is to let unit tests help you maintain your code and ensure it always works as intended. They enable you to:

  • clearly define implementation requirements,
  • better design your code and separate concerns,
  • discover issues you may introduce with your newer commits,
  • and give you confidence that your code works.

The best place to start is the Getting started page in the Jest documentation so you can try out these practices for yourself.

Do you have your own experience with testing code? I'd love to hear it, let me know on Twitter or join one of my Twitch streams.