Įsisavinkite pažangius „Jest“ testavimo metodus, kad sukurtumėte patikimesnę ir lengviau prižiūrimą programinę įrangą. Susipažinkite su imitavimo, momentinių kopijų testavimo, pasirinktinių derintojų ir kitomis technikomis, skirtomis pasaulinėms kūrėjų komandoms.
Jest: pažangūs testavimo modeliai tvirtai programinei įrangai
Šiuolaikinėje sparčiai besivystančioje programinės įrangos kūrimo aplinkoje itin svarbu užtikrinti kodo bazės patikimumą ir stabilumą. Nors „Jest“ tapo „de facto“ standartu „JavaScript“ testavimui, peržengus paprastų vienetų testų ribas, atsiveria naujas pasitikėjimo jūsų programomis lygis. Šiame įraše gilinamasi į pažangius „Jest“ testavimo modelius, kurie yra būtini kuriant tvirtą programinę įrangą ir yra skirti pasaulinei programuotojų auditorijai.
Kodėl verta peržengti paprastų vienetų testų ribas?
Paprasti vienetų testai patikrina atskirus komponentus izoliuotai. Tačiau realios programos yra sudėtingos sistemos, kuriose komponentai sąveikauja. Pažangūs testavimo modeliai sprendžia šias problemas, leisdami mums:
- Simuliuoti sudėtingas priklausomybes.
- Patikimai fiksuoti vartotojo sąsajos pakeitimus.
- Rašyti išraiškingesnius ir lengviau prižiūrimus testus.
- Pagerinti testų aprėptį ir pasitikėjimą integracijos taškuose.
- Palengvinti testais pagrįsto kūrimo (TDD) ir elgsena pagrįsto kūrimo (BDD) darbo eigas.
Imitavimo (Mocking) ir šnipinėjimo (Spies) įvaldymas
Imitavimas (angl. mocking) yra labai svarbus norint išskirti testuojamą vienetą, pakeičiant jo priklausomybes kontroliuojamais pakaitalais. „Jest“ tam suteikia galingus įrankius:
jest.fn()
: imitavimo ir šnipinėjimo pagrindas
jest.fn()
sukuria imituojančią funkciją. Galite sekti jos iškvietimus, argumentus ir grąžinamas reikšmes. Tai yra sudėtingesnių imitavimo strategijų pagrindas.
Pavyzdys: funkcijų iškvietimų sekimas
// component.js
export const fetchData = () => {
// Simuliuoja API užklausą
return Promise.resolve({ data: 'some data' });
};
export const processData = async (fetcher) => {
const result = await fetcher();
return `Processed: ${result.data}`;
};
// component.test.js
import { processData } from './component';
test('turėtų teisingai apdoroti duomenis', async () => {
const mockFetcher = jest.fn().mockResolvedValue({ data: 'mocked data' });
const result = await processData(mockFetcher);
expect(result).toBe('Processed: mocked data');
expect(mockFetcher).toHaveBeenCalledTimes(1);
expect(mockFetcher).toHaveBeenCalledWith();
});
jest.spyOn()
: stebėjimas nepakeičiant
jest.spyOn()
leidžia stebėti esamo objekto metodo iškvietimus, nebūtinai pakeičiant jo įgyvendinimą. Jei reikia, galite imituoti ir patį įgyvendinimą.
Pavyzdys: modulio metodo šnipinėjimas
// logger.js
export const logInfo = (message) => {
console.log(`INFO: ${message}`);
};
// service.js
import { logInfo } from './logger';
export const performTask = (taskName) => {
logInfo(`Starting task: ${taskName}`);
// ... užduoties logika ...
logInfo(`Task ${taskName} completed.`);
};
// service.test.js
import { performTask } from './service';
import * as logger from './logger';
test('turėtų registruoti užduoties pradžią ir pabaigą', () => {
const logSpy = jest.spyOn(logger, 'logInfo');
performTask('backup');
expect(logSpy).toHaveBeenCalledTimes(2);
expect(logSpy).toHaveBeenCalledWith('Starting task: backup');
expect(logSpy).toHaveBeenCalledWith('Task backup completed.');
logSpy.mockRestore(); // Svarbu atkurti pradinį įgyvendinimą
});
Modulių importavimo imitavimas
„Jest“ modulių imitavimo galimybės yra plačios. Galite imituoti ištisus modulius ar konkrečius eksportuojamus elementus.
Pavyzdys: išorinio API kliento imitavimas
// api.js
import axios from 'axios';
export const getUser = async (userId) => {
const response = await axios.get(`/api/users/${userId}`);
return response.data;
};
// user-service.js
import { getUser } from './api';
export const getUserFullName = async (userId) => {
const user = await getUser(userId);
return `${user.firstName} ${user.lastName}`;
};
// user-service.test.js
import { getUserFullName } from './user-service';
import * as api from './api';
// Imituojamas visas api modulis
jest.mock('./api');
test('turėtų gauti pilną vardą naudojant imituotą API', async () => {
// Imituojama konkreti funkcija iš imituoto modulio
api.getUser.mockResolvedValue({ id: 1, firstName: 'Ada', lastName: 'Lovelace' });
const fullName = await getUserFullName(1);
expect(fullName).toBe('Ada Lovelace');
expect(api.getUser).toHaveBeenCalledTimes(1);
expect(api.getUser).toHaveBeenCalledWith(1);
});
Automatinis ir rankinis imitavimas
„Jest“ automatiškai imituoja Node.js modulius. ES moduliams ar pasirinktiniams moduliams gali prireikti jest.mock()
. Norėdami daugiau kontrolės, galite sukurti __mocks__
katalogus.
Imituojantys įgyvendinimai
Galite pateikti pasirinktinius savo imitacijų įgyvendinimus.
Pavyzdys: imitavimas su pasirinktiniu įgyvendinimu
// math.js
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
// calculator.js
import { add, subtract } from './math';
export const calculate = (operation, a, b) => {
if (operation === 'add') {
return add(a, b);
} else if (operation === 'subtract') {
return subtract(a, b);
}
return null;
};
// calculator.test.js
import { calculate } from './calculator';
import * as math from './math';
// Imituojamas visas math modulis
jest.mock('./math');
test('turėtų atlikti sudėtį naudojant imituotą math add', () => {
// Pateikiamas imituojantis 'add' funkcijos įgyvendinimas
math.add.mockImplementation((a, b) => a + b + 10); // Pridedama 10 prie rezultato
math.subtract.mockReturnValue(5); // Atimtis taip pat imituojama
const result = calculate('add', 5, 3);
expect(math.add).toHaveBeenCalledWith(5, 3);
expect(result).toBe(18); // 5 + 3 + 10
const subResult = calculate('subtract', 10, 2);
expect(math.subtract).toHaveBeenCalledWith(10, 2);
expect(subResult).toBe(5);
});
Momentinių kopijų testavimas: vartotojo sąsajos ir konfigūracijos išsaugojimas
Momentinių kopijų testai (angl. snapshot tests) yra galinga funkcija, leidžianti fiksuoti jūsų komponentų ar konfigūracijų išvestį. Jie ypač naudingi testuojant vartotojo sąsają arba tikrinant sudėtingas duomenų struktūras.
Kaip veikia momentinių kopijų testavimas
Kai momentinės kopijos testas paleidžiamas pirmą kartą, „Jest“ sukuria .snap
failą su serijiniu testuojamos reikšmės atvaizdu. Vėlesnių paleidimų metu „Jest“ palygina dabartinę išvestį su išsaugota momentine kopija. Jei jos skiriasi, testas nepavyksta, įspėdamas jus apie nenumatytus pakeitimus. Tai neįkainojama aptinkant regresijas vartotojo sąsajos komponentuose skirtinguose regionuose ar lokalėse.
Pavyzdys: „React“ komponento momentinės kopijos kūrimas
Tarkime, turite „React“ komponentą:
// UserProfile.js
import React from 'react';
const UserProfile = ({ name, email, isActive }) => (
<div>
<h2>{name}</h2>
<p><strong>El. paštas:</strong> {email}</p>
<p><strong>Būsena:</strong> {isActive ? 'Aktyvus' : 'Neaktyvus'}</p>
</div>
);
export default UserProfile;
// UserProfile.test.js
import React from 'react';
import renderer from 'react-test-renderer'; // „React“ komponentų momentinėms kopijoms
import UserProfile from './UserProfile';
test('teisingai atvaizduoja UserProfile', () => {
const user = {
name: 'Jane Doe',
email: 'jane.doe@example.com',
isActive: true,
};
const component = renderer.create(
<UserProfile {...user} />
);
const tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
test('teisingai atvaizduoja neaktyvų UserProfile', () => {
const user = {
name: 'John Smith',
email: 'john.smith@example.com',
isActive: false,
};
const component = renderer.create(
<UserProfile {...user} />
);
const tree = component.toJSON();
expect(tree).toMatchSnapshot('inactive user profile'); // Pavadinta momentinė kopija
});
Paleidus testus, „Jest“ sukurs UserProfile.test.js.snap
failą. Atnaujinę komponentą, turėsite peržiūrėti pakeitimus ir galbūt atnaujinti momentinę kopiją, paleisdami „Jest“ su --updateSnapshot
arba -u
vėliavėle.
Geriausios momentinių kopijų testavimo praktikos
- Naudokite vartotojo sąsajos komponentams ir konfigūracijos failams: idealiai tinka užtikrinti, kad vartotojo sąsajos elementai būtų atvaizduojami kaip tikėtasi ir kad konfigūracija nepasikeistų netyčia.
- Atidžiai peržiūrėkite momentines kopijas: aklai nepriimkite momentinių kopijų atnaujinimų. Visada peržiūrėkite, kas pasikeitė, kad įsitikintumėte, jog pakeitimai yra sąmoningi.
- Venkite momentinių kopijų dažnai kintantiems duomenims: jei duomenys greitai keičiasi, momentinės kopijos gali tapti trapios ir sukelti per daug triukšmo.
- Naudokite pavadintas momentines kopijas: testuojant kelias komponento būsenas, pavadintos momentinės kopijos suteikia daugiau aiškumo.
Pasirinktiniai derintojai (Custom Matchers): testų skaitomumo gerinimas
„Jest“ integruoti derintojai yra platūs, tačiau kartais reikia patikrinti specifines sąlygas, kurių jie neapima. Pasirinktiniai derintojai leidžia jums sukurti savo patvirtinimo logiką, todėl jūsų testai tampa išraiškingesni ir lengviau skaitomi.
Pasirinktinių derintojų kūrimas
Galite išplėsti „Jest“ expect
objektą savo derintojais.
Pavyzdys: galiojančio el. pašto formato tikrinimas
Savo „Jest“ sąrankos faile (pvz., jest.setup.js
, sukonfigūruotame jest.config.js
):
// jest.setup.js
expect.extend({
toBeValidEmail(received) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const pass = emailRegex.test(received);
if (pass) {
return {
message: () => `expected ${received} not to be a valid email`,
pass: true,
};
} else {
return {
message: () => `expected ${received} to be a valid email`,
pass: false,
};
}
},
});
// Jūsų jest.config.js
// module.exports = { setupFilesAfterEnv: ['/jest.setup.js'] };
Savo testo faile:
// validation.test.js
test('turėtų patvirtinti el. pašto formatus', () => {
expect('test@example.com').toBeValidEmail();
expect('invalid-email').not.toBeValidEmail();
expect('another.test@sub.domain.co.uk').toBeValidEmail();
});
Pasirinktinių derintojų privalumai
- Pagerintas skaitomumas: testai tampa deklaratyvesni, nurodantys, *kas* yra testuojama, o ne *kaip*.
- Kodo pakartotinis naudojimas: venkite kartoti sudėtingą patvirtinimo logiką keliuose testuose.
- Domenui specifiniai patvirtinimai: pritaikykite patvirtinimus pagal savo programos specifinius domeno reikalavimus.
Asinchroninių operacijų testavimas
„JavaScript“ yra labai asinchroniška. „Jest“ suteikia puikų palaikymą testuojant pažadus (promises) ir async/await.
async/await
naudojimas
Tai yra moderniausias ir geriausiai skaitomas būdas testuoti asinchroninį kodą.
Pavyzdys: asinchroninės funkcijos testavimas
// dataService.js
export const fetchUserData = async (userId) => {
// Simuliuojamas duomenų gavimas po delsos
await new Promise(resolve => setTimeout(resolve, 50));
if (userId === 1) {
return { id: 1, name: 'Alice' };
} else {
throw new Error('User not found');
}
};
// dataService.test.js
import { fetchUserData } from './dataService';
test('teisingai gauna vartotojo duomenis', async () => {
const user = await fetchUserData(1);
expect(user).toEqual({ id: 1, name: 'Alice' });
});
test('meta klaidą neegzistuojančiam vartotojui', async () => {
await expect(fetchUserData(2)).rejects.toThrow('User not found');
});
.resolves
ir .rejects
naudojimas
Šie derintojai supaprastina pažadų įvykdymo ir atmetimo testavimą.
Pavyzdys: .resolves/.rejects naudojimas
// dataService.test.js (tęsinys)
test('gauna vartotojo duomenis su .resolves', () => {
return expect(fetchUserData(1)).resolves.toEqual({ id: 1, name: 'Alice' });
});
test('meta klaidą neegzistuojančiam vartotojui su .rejects', () => {
return expect(fetchUserData(2)).rejects.toThrow('User not found');
});
Laikmačių valdymas
Funkcijoms, kurios naudoja setTimeout
arba setInterval
, „Jest“ suteikia laikmačio valdymą.
Pavyzdys: laikmačių valdymas
// delayedGreeter.js
export const greetAfterDelay = (name, callback) => {
setTimeout(() => {
callback(`Hello, ${name}!`);
}, 1000);
};
// delayedGreeter.test.js
import { greetAfterDelay } from './delayedGreeter';
jest.useFakeTimers(); // Įgalinti netikrus laikmačius
test('pasveikina po delsos', () => {
const mockCallback = jest.fn();
greetAfterDelay('World', mockCallback);
// Pasukti laikmačius 1000ms į priekį
jest.advanceTimersByTime(1000);
expect(mockCallback).toHaveBeenCalledTimes(1);
expect(mockCallback).toHaveBeenCalledWith('Hello, World!');
});
// Atkurti tikrus laikmačius, jei reikia kitur
jest.useRealTimers();
Testų organizavimas ir struktūra
Augant jūsų testų rinkiniui, organizavimas tampa itin svarbus siekiant palaikomumo.
„Describe“ ir „it“ blokai
Naudokite describe
susijusiems testams grupuoti ir it
(arba test
) atskiriems testavimo atvejams. Ši struktūra atspindi programos moduliškumą.
Pavyzdys: struktūrizuoti testai
describe('Vartotojo autentifikavimo paslauga', () => {
let authService;
beforeEach(() => {
// Paruošti imitacijas ar paslaugų egzempliorius prieš kiekvieną testą
authService = require('./authService');
jest.spyOn(authService, 'login').mockImplementation(() => Promise.resolve({ token: 'fake_token' }));
});
afterEach(() => {
// Išvalyti imitacijas
jest.restoreAllMocks();
});
describe('prisijungimo funkcionalumas', () => {
it('turėtų sėkmingai prijungti vartotoją su galiojančiais prisijungimo duomenimis', async () => {
const result = await authService.login('user@example.com', 'password123');
expect(result.token).toBeDefined();
// ... daugiau patvirtinimų ...
});
it('turėtų nepavykti prisijungti su negaliojančiais prisijungimo duomenimis', async () => {
jest.spyOn(authService, 'login').mockRejectedValue(new Error('Invalid credentials'));
await expect(authService.login('user@example.com', 'wrong_password')).rejects.toThrow('Invalid credentials');
});
});
describe('atsijungimo funkcionalumas', () => {
it('turėtų išvalyti vartotojo sesiją', async () => {
// Testuoti atsijungimo logiką...
});
});
});
Sąrankos (Setup) ir išmontavimo (Teardown) kabliai
beforeAll
: paleidžiamas vieną kartą prieš visus testusdescribe
bloke.afterAll
: paleidžiamas vieną kartą po visų testųdescribe
bloke.beforeEach
: paleidžiamas prieš kiekvieną testądescribe
bloke.afterEach
: paleidžiamas po kiekvieno testodescribe
bloke.
Šie kabliai yra būtini norint paruošti imituojamus duomenis, duomenų bazių ryšius ar išvalyti resursus tarp testų.
Testavimas pasaulinei auditorijai
Kuriant programas pasaulinei auditorijai, testavimo aspektai išsiplečia:
Tarptautinimas (i18n) ir lokalizavimas (l10n)
Užtikrinkite, kad jūsų vartotojo sąsaja ir pranešimai teisingai prisitaikytų prie skirtingų kalbų ir regioninių formatų.
- Lokalizuotos vartotojo sąsajos momentinės kopijos: testuokite, ar skirtingų kalbų vartotojo sąsajos versijos atvaizduojamos teisingai, naudodami momentinių kopijų testus.
- Lokalės duomenų imitavimas: imituokite bibliotekas, tokias kaip
react-intl
ari18next
, kad išbandytumėte komponentų elgseną su skirtingos lokalės pranešimais. - Datos, laiko ir valiutos formatavimas: testuokite, ar tai tvarkoma teisingai, naudojant pasirinktinius derintojus arba imituojant internacionalizacijos bibliotekas. Pavyzdžiui, patikrinkite, ar Vokietijai formatuota data (DD.MM.YYYY) atrodo kitaip nei JAV (MM/DD/YYYY).
Pavyzdys: lokalizuoto datos formato testavimas
// dateUtils.js
export const formatLocalizedDate = (date, locale) => {
return new Intl.DateTimeFormat(locale, { year: 'numeric', month: 'numeric', day: 'numeric' }).format(date);
};
// dateUtils.test.js
import { formatLocalizedDate } from './dateUtils';
test('teisingai formatuoja datą JAV lokalei', () => {
const date = new Date(2023, 10, 15); // 2023 m. lapkričio 15 d.
expect(formatLocalizedDate(date, 'en-US')).toBe('11/15/2023');
});
test('teisingai formatuoja datą Vokietijos lokalei', () => {
const date = new Date(2023, 10, 15);
expect(formatLocalizedDate(date, 'de-DE')).toBe('15.11.2023');
});
Laiko juostų išmanymas
Testuokite, kaip jūsų programa tvarko skirtingas laiko juostas, ypač funkcijoms, tokioms kaip planavimas ar realaus laiko atnaujinimai. Sistemos laikrodžio imitavimas arba bibliotekų, kurios abstrahuoja laiko juostas, naudojimas gali būti naudingas.
Kultūriniai duomenų niuansai
Apsvarstykite, kaip skaičiai, valiutos ir kitos duomenų reprezentacijos gali būti suvokiamos ar tikimasi skirtingai įvairiose kultūrose. Čia ypač naudingi gali būti pasirinktiniai derintojai.
Pažangios technikos ir strategijos
Testais pagrįstas kūrimas (TDD) ir elgsena pagrįstas kūrimas (BDD)
„Jest“ puikiai dera su TDD („Red-Green-Refactor“) ir BDD („Given-When-Then“) metodologijomis. Rašykite testus, kurie apibūdina norimą elgseną, prieš rašydami įgyvendinimo kodą. Tai užtikrina, kad kodas nuo pat pradžių rašomas atsižvelgiant į testuojamumą.
Integracinis testavimas su „Jest“
Nors „Jest“ puikiai tinka vienetų testams, jį taip pat galima naudoti integraciniams testams. Gali padėti mažesnio skaičiaus priklausomybių imitavimas arba tokių įrankių kaip „Jest“ runInBand
parinkties naudojimas.
Pavyzdys: API sąveikos testavimas (supaprastintas)
// apiService.js
import axios from 'axios';
const API_BASE_URL = 'https://api.example.com';
export const createProduct = async (productData) => {
const response = await axios.post(`${API_BASE_URL}/products`, productData);
return response.data;
};
// apiService.test.js (Integracinis testas)
import axios from 'axios';
import { createProduct } from './apiService';
// Imituojamas axios integraciniams testams, kad būtų galima kontroliuoti tinklo lygmenį
jest.mock('axios');
test('sukuriamas produktas per API', async () => {
const mockProduct = { id: 1, name: 'Gadget' };
const responseData = { success: true, product: mockProduct };
axios.post.mockResolvedValue({
data: responseData,
status: 201,
headers: { 'content-type': 'application/json' },
});
const newProductData = { name: 'Gadget', price: 99.99 };
const result = await createProduct(newProductData);
expect(axios.post).toHaveBeenCalledWith(`${process.env.API_BASE_URL || 'https://api.example.com'}/products`, newProductData);
expect(result).toEqual(responseData);
});
Lygiagretumas ir konfigūravimas
„Jest“ gali paleisti testus lygiagrečiai, kad pagreitintų vykdymą. Sukonfigūruokite tai savo jest.config.js
faile. Pavyzdžiui, nustatymas maxWorkers
kontroliuoja lygiagrečių procesų skaičių.
Aprėpties ataskaitos
Naudokite „Jest“ integruotą aprėpties ataskaitų teikimą, kad nustatytumėte kodo bazės dalis, kurios nėra testuojamos. Paleiskite testus su --coverage
, kad sugeneruotumėte išsamias ataskaitas.
jest --coverage
Aprėpties ataskaitų peržiūra padeda užtikrinti, kad jūsų pažangūs testavimo modeliai veiksmingai apimtų kritinę logiką, įskaitant internacionalizavimo ir lokalizavimo kodo kelius.
Išvada
Pažangių „Jest“ testavimo modelių įvaldymas yra svarbus žingsnis kuriant patikimą, lengvai prižiūrimą ir aukštos kokybės programinę įrangą pasaulinei auditorijai. Efektyviai naudodami imitavimą, momentinių kopijų testavimą, pasirinktinius derintojus ir asinchroninio testavimo technikas, galite sustiprinti savo testų rinkinio tvirtumą ir įgyti didesnį pasitikėjimą savo programos elgsena įvairiuose scenarijuose ir regionuose. Šių modelių taikymas įgalina kūrėjų komandas visame pasaulyje teikti išskirtines vartotojų patirtis.
Pradėkite taikyti šias pažangias technikas savo darbo eigoje jau šiandien, kad pakeltumėte savo „JavaScript“ testavimo praktikas į aukštesnį lygį.