Testowanie modułów Node.js z użyciem Chai, Sinon i Mocha
Zaktualizowaliśmy ten tekst dla Ciebie!
Data aktualizacji: 26.12.2024
Autor aktualizacji: Adam Olszewski
Wprowadzenie
W tym artykule pokażę, jak pisać testy jednostkowe dla modułów Node.js i ich zależności. Testowanie jednostkowe jest kluczowe, aby małe zmiany w kodzie nie zepsuły aplikacji. Błąd w kluczowym systemie może prowadzić do poważnych konsekwencji, jak w przypadku awarii systemu bagażowego na lotnisku, co może skutkować niezadowoleniem klientów, pozwami i karami finansowymi. Pisanie efektywnych testów jednostkowych, testów integracyjnych i testów end-to-end zmniejsza ryzyko takich awarii.
Ważne jest, aby pisać sensowne testy, a nie tylko te, które zwiększają procent pokrycia kodu. Dobre testy pomagają zapobiec wprowadzeniu uszkodzonego kodu do produkcji, zapewniając, że aplikacja zachowuje się zgodnie z oczekiwaniami nawet po wprowadzeniu zmian. Z mojego doświadczenia wynika, że dobrze napisane testy jednostkowe są zbawienne, pomagając zapobiec katastrofom przy wdrożeniu zmian.
Rozpoczęcie pracy
Zacznijmy od utworzenia katalogu projektu i zainstalowania wymaganych bibliotek. W skład tych bibliotek wchodzą frameworki testowe Mocha, Chai i Sinon. Mocha jest narzędziem do uruchamiania testów, Chai zapewnia bibliotekę do asercji, a Sinon umożliwia tworzenie mocków i stubów zależności.
Inicjalizacja projektu
Utwórz nowy katalog i zainicjuj package.json:
bash
mkdir node-test-tutorial
cd node-test-tutorial
npm init -y
Instalacja zależności
Zainstaluj niezbędne biblioteki do testowania:
bash
npm install mocha chai sinon node-fetch @babel/core @babel/cli @babel/preset-env --save-dev
Ta komenda instaluje Mocha, Chai, Sinon, Babel dla ES6+
Konfiguracja package.json
Zmień sekcję scripts w package.json, aby dodać polecenia do uruchamiania aplikacji i testów:
json
"scripts": {
"start": "babel-node app/app.js --presets @babel/preset-env",
"test": "mocha --require @babel/register tests/**/*.spec.js"
}
Teraz możesz uruchomić aplikację za pomocą npm start i testy za pomocą npm test.
Pisanie kodu aplikacji
Stwórzmy małą aplikację, która pobiera dane pogodowe z zewnętrznego API (OpenWeatherMap) i zwraca odpowiednie komunikaty w zależności od temperatury.
api.js (odpowiada za pobieranie danych pogodowych)
javascript
import fetch from 'node-fetch';
/**
* Fetches weather data for a given city.
* @param {string} city The city name
* @returns {Promise<object>} The weather data
*/
export async function getWeatherData(city) {
const url = `http://api.openweathermap.org/data/2.5/weather?appid=YOUR_API_KEY&units=metric&q=${encodeURIComponent(city)}`;
try {
const response = await fetch(url);
if (!response.ok) throw new Error('API Error');
return await response.json();
} catch (error) {
throw new Error('Network or API Error');
}
}
app.js (logika głównej aplikacji wykorzystująca dane pogodowe)
javascript
import { getWeatherData } from './api';
export const TEMPERATURE_FREEZING = 'Temperature is below 0, remember to take a coat and scarf!';
export const TEMPERATURE_COLD = 'Temperature is below 10, remember to take a coat!';
export const TEMPERATURE_OK = 'Temperature is okay today.';
export const TEMPERATURE_HOT = 'Warning! Temperature is above 30 degrees, watch for sunburn!';
export const TEMPERATURE_ERROR = 'API Error. Unable to retrieve temperature.';
export const NO_CITY = 'No city specified.';
/**
* Returns a weather-related message based on the temperature.
* @param {number} temperature The current temperature in Celsius
* @returns {string} The appropriate message
*/
export const getTemperatureMessage = (temperature) => {
if (temperature === undefined) return TEMPERATURE_ERROR;
if (temperature < 0) return TEMPERATURE_FREEZING;
if (temperature < 10) return TEMPERATURE_COLD;
if (temperature < 30) return TEMPERATURE_OK;
return TEMPERATURE_HOT;
};
/**
* Fetches weather data for a specified city and returns a temperature message.
* @param {string} city The city name
* @returns {Promise<string>} The temperature-related message
*/
export const getWeatherInfo = async (city) => {
if (!city) return NO_CITY;
try {
const weather = await getWeatherData(city);
return getTemperatureMessage(weather.main.temp);
} catch (error) {
return TEMPERATURE_ERROR;
}
};
Pisanie testów
Teraz napiszemy testy dla metod w app.js. Użyjemy Mocha jako narzędzia do uruchamiania testów, Chai do asercji, a Sinon do tworzenia mocków dla zewnętrznych wywołań API.
Test dla getTemperatureMessage (sprawdzanie różnych zakresów temperatury)
javascript
import * as app from '../app/app';
const { expect } = require('chai');
describe('App.js Tests', () => {
describe('Check Temperature Messages', () => {
it('Temperature not specified', () => {
const message = app.getTemperatureMessage();
expect(message).to.equal(app.TEMPERATURE_ERROR);
});
it('Temperature below 0°C', () => {
const message = app.getTemperatureMessage(-5);
expect(message).to.equal(app.TEMPERATURE_FREEZING);
});
it('Temperature between 0°C and 10°C', () => {
const message = app.getTemperatureMessage(5);
expect(message).to.equal(app.TEMPERATURE_COLD);
});
it('Temperature between 10°C and 30°C', () => {
const message = app.getTemperatureMessage(20);
expect(message).to.equal(app.TEMPERATURE_OK);
});
it('Temperature above 30°C', () => {
const message = app.getTemperatureMessage(35);
expect(message).to.equal(app.TEMPERATURE_HOT);
});
});
});
Test dla getWeatherInfo z użyciem stubowania Sinon (mockowanie zewnętrznego API)
javascript
import * as app from '../app/app';
import * as api from '../app/api';
const { expect } = require('chai');
const sinon = require('sinon');
describe('getWeatherInfo', () => {
let weatherApiCallStub;
beforeEach(() => {
const fakeResponse = { main: { temp: 15 } };
weatherApiCallStub = sinon.stub(api, 'getWeatherData').callsFake(() => Promise.resolve(fakeResponse));
});
afterEach(() => {
weatherApiCallStub.restore();
});
it('should return the correct temperature message', async () => {
const message = await app.getWeatherInfo('London');
expect(message).to.equal(app.TEMPERATURE_OK);
sinon.assert.calledOnce(weatherApiCallStub);
});
it('should handle API errors gracefully', async () => {
weatherApiCallStub.callsFake(() => Promise.reject(new Error('API Error')));
const message = await app.getWeatherInfo('InvalidCity');
expect(message).to.equal(app.TEMPERATURE_ERROR);
});
it('should handle no city specified', async () => {
const message = await app.getWeatherInfo();
expect(message).to.equal(app.NO_CITY);
sinon.assert.notCalled(weatherApiCallStub);
});
});
Podsumowanie
Dzięki połączeniu Mocha, Chai i Sinon udało nam się napisać testy jednostkowe dla naszej aplikacji Node.js. Przetestowaliśmy różne scenariusze, w tym obsługę odpowiedzi z zewnętrznego API za pomocą stubowania Sinon, upewniając się, że aplikacja działa poprawnie w różnych warunkach.
Dobre testy jednostkowe mogą zapobiec katastrofalnym błędom podczas rozwoju i wdrażania aplikacji. Ułatwiają zarządzanie zmianami w kodzie i zapewniają, że aplikacja nadal spełnia wymagania biznesowe. Pamiętaj: nieidealne testy, które uruchamiane są regularnie, są lepsze niż perfekcyjne testy, które nigdy nie zostaną napisane.
Szczęśliwego kodowania i testowania!
Poznaj mageek of j‑labs i daj się zadziwić, jak może wyglądać praca z j‑People!
Skontaktuj się z nami


