Slovenščina

Obvladajte razvoj, voden s testi (TDD), v JavaScriptu. Vodnik zajema cikel Rdeča-Zelena-Prenova, implementacijo z Jestom in najboljše prakse.

Razvoj, voden s testi, v JavaScriptu: Celovit vodnik za globalne razvijalce

Predstavljajte si naslednji scenarij: vaša naloga je spremeniti ključen del kode v velikem, starem sistemu. Občutite strah. Ali bo vaša sprememba pokvarila kaj drugega? Kako ste lahko prepričani, da sistem še vedno deluje, kot je predvideno? Ta strah pred spremembami je pogosta težava pri razvoju programske opreme, ki pogosto vodi v počasen napredek in krhke aplikacije. Kaj pa, če bi obstajal način za samozavestno gradnjo programske opreme, ki ustvari varnostno mrežo, ki ujame napake, preden sploh pridejo v produkcijo? To je obljuba razvoja, vodenega s testi (Test-Driven Development - TDD).

TDD ni zgolj tehnika testiranja; je discipliniran pristop k načrtovanju in razvoju programske opreme. Obrne tradicionalni model "napiši kodo, nato testiraj". Pri TDD napišete test, ki pade preden napišete produkcijsko kodo, da test uspe. Ta preprosta inverzija ima globoke posledice za kakovost kode, zasnovo in vzdrževanje. Ta vodnik bo ponudil celovit, praktičen pogled na implementacijo TDD v JavaScriptu, namenjen globalnemu občinstvu profesionalnih razvijalcev.

Kaj je razvoj, voden s testi (TDD)?

V svojem bistvu je razvoj, voden s testi, razvojni proces, ki temelji na ponavljanju zelo kratkega razvojnega cikla. Namesto da bi pisali funkcionalnosti in jih nato testirali, TDD vztraja, da se test napiše najprej. Ta test bo neizogibno padel, ker funkcionalnost še ne obstaja. Naloga razvijalca je nato napisati najpreprostejšo možno kodo, da ta specifičen test uspe. Ko uspe, se koda očisti in izboljša. Ta temeljni cikel je znan kot cikel "Rdeča-Zelena-Prenova".

Ritem TDD: Rdeča-Zelena-Prenova

Ta tristopenjski cikel je srčni utrip TDD. Razumevanje in prakticiranje tega ritma je temelj za obvladovanje tehnike.

Ko je cikel zaključen za en majhen del funkcionalnosti, začnete znova z novim testom, ki pade, za naslednji del.

Trije zakoni TDD

Robert C. Martin (pogosto znan kot "stric Bob"), ključna osebnost v gibanju agilne programske opreme, je opredelil tri preprosta pravila, ki kodificirajo disciplino TDD:

  1. Ne smete pisati nobene produkcijske kode, razen če je namenjena temu, da uspe enota, ki je padla na testu.
  2. Ne smete napisati več enotnega testa, kot je potrebno, da pade; in napake pri prevajanju so napake.
  3. Ne smete napisati več produkcijske kode, kot je potrebno, da uspe en sam test, ki je padel.

Sledenje tem zakonom vas prisili v cikel Rdeča-Zelena-Prenova in zagotavlja, da je 100 % vaše produkcijske kode napisane za izpolnitev specifične, preizkušene zahteve.

Zakaj bi morali sprejeti TDD? Globalni poslovni primer

Čeprav TDD ponuja ogromne koristi posameznim razvijalcem, se njegova prava moč uresniči na ravni ekipe in podjetja, zlasti v globalno porazdeljenih okoljih.

Nastavitev vašega JavaScript TDD okolja

Za začetek z TDD v JavaScriptu potrebujete nekaj orodij. Sodoben ekosistem JavaScripta ponuja odlične izbire.

Osnovne komponente testnega sklopa

Zaradi svoje preprostosti in narave "vse v enem" bomo za naše primere uporabili Jest. Je odlična izbira za ekipe, ki iščejo izkušnjo "brez konfiguracije".

Postopek nastavitve po korakih z Jestom

Nastavimo nov projekt za TDD.

1. Inicializirajte svoj projekt: Odprite terminal in ustvarite novo mapo za projekt.

mkdir js-tdd-project
cd js-tdd-project
npm init -y

2. Namestite Jest: Dodajte Jest v svoj projekt kot razvojno odvisnost.

npm install --save-dev jest

3. Konfigurirajte skript za testiranje: Odprite datoteko `package.json`. Poiščite razdelek `"scripts"` in spremenite skript `"test"`. Zelo priporočljivo je dodati tudi skript `"test:watch"`, ki je neprecenljiv za delovni tok TDD.

"scripts": {
  "test": "jest",
  "test:watch": "jest --watchAll"
}

Zastavica `--watchAll` pove Jestu, naj samodejno ponovno zažene teste, vsakič ko se datoteka shrani. To zagotavlja takojšnjo povratno informacijo, kar je popolno za cikel Rdeča-Zelena-Prenova.

To je to! Vaše okolje je pripravljeno. Jest bo samodejno našel testne datoteke, ki se imenujejo `*.test.js`, `*.spec.js` ali se nahajajo v mapi `__tests__`.

TDD v praksi: Gradnja modula `CurrencyConverter`

Uporabimo cikel TDD na praktičnem, globalno razumljivem problemu: pretvorbi denarja med valutami. Korak za korakom bomo zgradili modul `CurrencyConverter`.

Ponavljanje 1: Preprosta pretvorba s fiksnim tečajem

🔴 RDEČA: Napišite prvi test, ki pade

Naša prva zahteva je pretvoriti določen znesek iz ene valute v drugo z uporabo fiksnega tečaja. Ustvarite novo datoteko z imenom `CurrencyConverter.test.js`.

// CurrencyConverter.test.js
const CurrencyConverter = require('./CurrencyConverter');

describe('CurrencyConverter', () => {
  it('should convert an amount from USD to EUR correctly', () => {
    // Priprava (Arrange)
    const amount = 10; // 10 USD
    const expected = 9.2; // Ob predpostavki fiksnega tečaja 1 USD = 0.92 EUR

    // Izvedba (Act)
    const result = CurrencyConverter.convert(amount, 'USD', 'EUR');

    // Preverjanje (Assert)
    expect(result).toBe(expected);
  });
});

Sedaj zaženite opazovalca testov v terminalu:

npm run test:watch

Test bo spektakularno padel. Jest bo poročal nekaj podobnega `TypeError: Cannot read properties of undefined (reading 'convert')`. To je naše RDEČE stanje. Test pade, ker `CurrencyConverter` ne obstaja.

🟢 ZELENA: Napišite najpreprostejšo kodo za uspeh

Sedaj pa poskrbimo, da bo test uspešen. Ustvarite `CurrencyConverter.js`.

// CurrencyConverter.js
const rates = {
  USD: {
    EUR: 0.92
  }
};

const CurrencyConverter = {
  convert(amount, from, to) {
    return amount * rates[from][to];
  }
};

module.exports = CurrencyConverter;

Takoj ko shranite to datoteko, bo Jest ponovno zagnal test in ta bo postal ZELEN. Napisali smo absolutni minimum kode, da bi zadostili zahtevi testa.

🔵 PRENOVA: Izboljšajte kodo

Koda je preprosta, vendar že lahko razmišljamo o izboljšavah. Vgnezdnjen objekt `rates` je malce tog. Zaenkrat je dovolj čist. Najpomembneje je, da imamo delujočo funkcionalnost, zaščiteno s testom. Pojdimo na naslednjo zahtevo.

Ponavljanje 2: Obravnavanje neznanih valut

🔴 RDEČA: Napišite test za neveljavno valuto

Kaj naj se zgodi, če poskusimo pretvoriti v valuto, ki je ne poznamo? Verjetno bi moralo sprožiti napako. Opredelimo to obnašanje v novem testu v datoteki `CurrencyConverter.test.js`.

// V datoteki CurrencyConverter.test.js, znotraj bloka describe

it('should throw an error for unknown currencies', () => {
  // Priprava (Arrange)
  const amount = 10;

  // Izvedba in preverjanje (Act & Assert)
  // Klic funkcije ovijemo v puščično funkcijo, da Jestova metoda toThrow deluje.
  expect(() => {
    CurrencyConverter.convert(amount, 'USD', 'XYZ');
  }).toThrow('Unknown currency: XYZ');
});

Shranite datoteko. Zaganjalnik testov takoj pokaže novo napako. Je RDEČA, ker naša koda ne sproži napake; poskuša dostopiti do `rates['USD']['XYZ']`, kar povzroči `TypeError`. Naš novi test je pravilno odkril to pomanjkljivost.

🟢 ZELENA: Poskrbite, da bo nov test uspešen

Spremenimo `CurrencyConverter.js` in dodajmo preverjanje.

// CurrencyConverter.js
const rates = {
  USD: {
    EUR: 0.92,
    GBP: 0.80
  },
  EUR: {
    USD: 1.08
  }
};

const CurrencyConverter = {
  convert(amount, from, to) {
    if (!rates[from] || !rates[from][to]) {
      // Določimo, katera valuta je neznana za boljše sporočilo o napaki
      const unknownCurrency = !rates[from] ? from : to;
      throw new Error(`Unknown currency: ${unknownCurrency}`);
    }
    return amount * rates[from][to];
  }
};

module.exports = CurrencyConverter;

Shranite datoteko. Oba testa sta zdaj uspešna. Vrnili smo se v ZELENO stanje.

🔵 PRENOVA: Počistite kodo

Naša funkcija `convert` raste. Logika preverjanja je pomešana z izračunom. Preverjanje bi lahko izvlekli v ločeno zasebno funkcijo za izboljšanje berljivosti, a zaenkrat je še vedno obvladljivo. Ključno je, da imamo svobodo pri teh spremembah, ker nam bodo naši testi povedali, če kaj pokvarimo.

Ponavljanje 3: Asinhrono pridobivanje tečajev

Fiksno vpisovanje tečajev ni realistično. Prenovimo naš modul, da bo pridobival tečaje iz (posnetega) zunanjega API-ja.

🔴 RDEČA: Napišite asinhroni test, ki posnema klic API-ja

Najprej moramo prestrukturirati naš pretvornik. Zdaj bo moral biti razred, ki ga lahko instanciramo, morda z odjemalcem API-ja. Prav tako bomo morali posnemati API `fetch`. Z Jestom je to enostavno.

Prepišimo našo testno datoteko, da bo ustrezala tej novi, asinhroni realnosti. Začeli bomo s ponovnim testiranjem srečne poti.

// CurrencyConverter.test.js
const CurrencyConverter = require('./CurrencyConverter');

// Posnemamo zunanjo odvisnost
global.fetch = jest.fn();

beforeEach(() => {
  // Počistimo zgodovino posnetkov pred vsakim testom
  fetch.mockClear();
});

describe('CurrencyConverter', () => {
  it('should fetch rates and convert correctly', async () => {
    // Priprava (Arrange)
    // Posnemamo uspešen odgovor API-ja
    fetch.mockResolvedValueOnce({
      json: () => Promise.resolve({ rates: { EUR: 0.92 } })
    });

    const converter = new CurrencyConverter('https://api.exchangerates.com');
    const amount = 10; // 10 USD

    // Izvedba (Act)
    const result = await converter.convert(amount, 'USD', 'EUR');

    // Preverjanje (Assert)
    expect(result).toBe(9.2);
    expect(fetch).toHaveBeenCalledTimes(1);
    expect(fetch).toHaveBeenCalledWith('https://api.exchangerates.com/latest?base=USD');
  });

  // Dodali bi tudi teste za napake API-ja itd.
});

Zagon tega bo povzročil morje RDEČE barve. Naš stari `CurrencyConverter` ni razred, nima asinhrone metode `async` in ne uporablja `fetch`.

🟢 ZELENA: Implementirajte asinhrono logiko

Sedaj prepišimo `CurrencyConverter.js`, da bo ustrezal zahtevam testa.

// CurrencyConverter.js
class CurrencyConverter {
  constructor(apiUrl) {
    this.apiUrl = apiUrl;
  }

  async convert(amount, from, to) {
    const response = await fetch(`${this.apiUrl}/latest?base=${from}`);
    if (!response.ok) {
      throw new Error('Failed to fetch exchange rates.');
    }

    const data = await response.json();
    const rate = data.rates[to];

    if (!rate) {
      throw new Error(`Unknown currency: ${to}`);
    }

    // Preprosto zaokroževanje za preprečevanje težav s plavajočo vejico v testih
    const convertedAmount = amount * rate;
    return parseFloat(convertedAmount.toFixed(2));
  }
}

module.exports = CurrencyConverter;

Ko shranite, bi moral test postati ZELEN. Upoštevajte, da smo dodali tudi logiko zaokroževanja za obravnavo netočnosti s plavajočo vejico, kar je pogosta težava pri finančnih izračunih.

🔵 PRENOVA: Izboljšajte asinhrono kodo

Metoda `convert` počne veliko: pridobivanje podatkov, obravnavanje napak, razčlenjevanje in računanje. To bi lahko prenovili tako, da ustvarimo ločen razred `RateFetcher`, ki bi bil odgovoren samo za komunikacijo z API-jem. Naš `CurrencyConverter` bi nato uporabil ta pridobivalnik. To sledi načelu ene same odgovornosti (Single Responsibility Principle) in olajša testiranje ter vzdrževanje obeh razredov. TDD nas vodi k tej čistejši zasnovi.

Pogosti vzorci in prototipci TDD

Medtem ko boste vadili TDD, boste odkrili vzorce, ki dobro delujejo, in prototipce, ki povzročajo trenje.

Dobri vzorci, ki jim je vredno slediti

Prototipci, ki se jim je treba izogibati

TDD v širšem razvojnem življenjskem ciklu

TDD ne obstaja v vakuumu. Čudovito se povezuje s sodobnimi agilnimi in DevOps praksami, zlasti za globalne ekipe.

Zaključek: Vaša pot z TDD

Razvoj, voden s testi, je več kot le strategija testiranja – je paradigmatski premik v našem pristopu k razvoju programske opreme. Spodbuja kulturo kakovosti, zaupanja in sodelovanja. Cikel Rdeča-Zelena-Prenova zagotavlja stalen ritem, ki vas vodi k čisti, robustni in vzdržljivi kodi. Nastali nabor testov postane varnostna mreža, ki ščiti vašo ekipo pred regresijami, in živa dokumentacija, ki uvaja nove člane.

Krivulja učenja se lahko zdi strma, začetni tempo pa počasnejši. Toda dolgoročne koristi v zmanjšanem času za odpravljanje napak, izboljšani zasnovi programske opreme in povečani samozavesti razvijalcev so neizmerne. Pot do obvladovanja TDD je pot discipline in prakse.

Začnite danes. Izberite eno majhno, nekritično funkcionalnost v vašem naslednjem projektu in se zavežite procesu. Najprej napišite test. Opazujte, kako pade. Naredite, da uspe. In potem, kar je najpomembneje, prenovite. Izkusite samozavest, ki jo prinaša zelen nabor testov, in kmalu se boste spraševali, kako ste kdaj koli gradili programsko opremo na drugačen način.