Latviešu

Apgūstiet uz testiem balstītu izstrādi (TDD) JavaScript valodā. Šis visaptverošais ceļvedis aptver Sarkans-Zaļš-Refaktorēt ciklu, praktisku ieviešanu ar Jest un labākās prakses mūsdienu izstrādē.

Uz testiem balstīta izstrāde (TDD) JavaScript valodā: visaptverošs ceļvedis globāliem izstrādātājiem

Iztēlojieties šādu scenāriju: jums ir uzdots modificēt kritiski svarīgu koda daļu lielā, mantotā sistēmā. Jūs pārņem bažas. Vai jūsu veiktās izmaiņas kaut ko salauzīs? Kā jūs varat būt pārliecināts, ka sistēma joprojām darbojas, kā paredzēts? Šīs bailes no izmaiņām ir izplatīta problēma programmatūras izstrādē, kas bieži noved pie lēna progresa un trauslām lietojumprogrammām. Bet ko tad, ja būtu veids, kā veidot programmatūru ar pārliecību, radot drošības tīklu, kas notver kļūdas, pirms tās nonāk produkcijā? To sola uz testiem balstīta izstrāde (Test-Driven Development - TDD).

TDD nav tikai testēšanas tehnika; tā ir disciplinēta pieeja programmatūras projektēšanai un izstrādei. Tā apgriež tradicionālo modeli "uzraksti kodu, tad testē". Ar TDD jūs uzrakstāt testu, kas neizdodas pirms jūs rakstāt produkcijas kodu, lai tas izdotos. Šai vienkāršajai inversijai ir dziļa ietekme uz koda kvalitāti, dizainu un uzturamību. Šis ceļvedis sniegs visaptverošu, praktisku ieskatu TDD ieviešanā JavaScript valodā, kas paredzēts profesionālu izstrādātāju globālajai auditorijai.

Kas ir uz testiem balstīta izstrāde (TDD)?

Savā būtībā uz testiem balstīta izstrāde ir izstrādes process, kas balstās uz ļoti īsa izstrādes cikla atkārtošanu. Tā vietā, lai rakstītu funkcionalitāti un pēc tam to testētu, TDD uzstāj, ka tests tiek uzrakstīts vispirms. Šis tests neizbēgami neizdosies, jo funkcionalitāte vēl nepastāv. Izstrādātāja uzdevums ir uzrakstīt vienkāršāko iespējamo kodu, lai šis konkrētais tests izdotos. Kad tas izdodas, kods tiek sakopts un uzlabots. Šo fundamentālo ciklu sauc par "Sarkans-Zaļš-Refaktorēt" ciklu.

TDD ritms: Sarkans-Zaļš-Refaktorēt

Šis trīs soļu cikls ir TDD sirdspuksti. Šī ritma izpratne un praktizēšana ir fundamentāla, lai apgūtu šo tehniku.

Kad cikls ir pabeigts vienai mazai funkcionalitātes daļai, jūs sākat no jauna ar jaunu neizpildāmu testu nākamajai daļai.

Trīs TDD likumi

Roberts C. Mārtins (bieži pazīstams kā "onkulis Bobs"), Agile programmatūras kustības galvenā figūra, definēja trīs vienkāršus noteikumus, kas kodificē TDD disciplīnu:

  1. Jūs nedrīkstat rakstīt produkcijas kodu, ja vien tas nav paredzēts, lai izpildītu neizdevušos vienībtestu.
  2. Jūs nedrīkstat rakstīt vairāk vienībtesta koda, nekā nepieciešams, lai tas neizdotos; un kompilācijas kļūdas arī ir neizdošanās.
  3. Jūs nedrīkstat rakstīt vairāk produkcijas koda, nekā nepieciešams, lai izpildītu vienu neizdevušos vienībtestu.

Šo likumu ievērošana piespiež jūs sekot Sarkans-Zaļš-Refaktorēt ciklam un nodrošina, ka 100% jūsu produkcijas koda ir rakstīts, lai apmierinātu konkrētu, pārbaudītu prasību.

Kāpēc jums vajadzētu pieņemt TDD? Globālais biznesa pamatojums

Lai gan TDD piedāvā milzīgas priekšrocības individuāliem izstrādātājiem, tā patiesais spēks tiek realizēts komandas un biznesa līmenī, īpaši globāli sadalītās vidēs.

Jūsu JavaScript TDD vides iestatīšana

Lai sāktu darbu ar TDD JavaScript valodā, jums ir nepieciešami daži rīki. Mūsdienu JavaScript ekosistēma piedāvā lieliskas izvēles.

Testēšanas komplekta pamatkomponentes

Tā vienkāršības un "viss vienā" dabas dēļ mēs savos piemēros izmantosim Jest. Tā ir lieliska izvēle komandām, kas meklē "nulles konfigurācijas" pieredzi.

Soli pa solim iestatīšana ar Jest

Iestatīsim jaunu projektu priekš TDD.

1. Inicializējiet savu projektu: Atveriet termināli un izveidojiet jaunu projekta direktoriju.

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

2. Instalējiet Jest: Pievienojiet Jest savam projektam kā izstrādes atkarību.

npm install --save-dev jest

3. Konfigurējiet testa skriptu: Atveriet savu `package.json` failu. Atrodiet `"scripts"` sadaļu un modificējiet `"test"` skriptu. Ir arī ļoti ieteicams pievienot `"test:watch"` skriptu, kas ir nenovērtējams TDD darbplūsmai.

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

`--watchAll` karodziņš liek Jest automātiski atkārtoti palaist testus, ikreiz, kad tiek saglabāts fails. Tas nodrošina tūlītēju atgriezenisko saiti, kas ir ideāli piemērota Sarkans-Zaļš-Refaktorēt ciklam.

Tas arī viss! Jūsu vide ir gatava. Jest automātiski atradīs testu failus ar nosaukumu `*.test.js`, `*.spec.js` vai tos, kas atrodas `__tests__` direktorijā.

TDD in Practice: Building a `CurrencyConverter` Module

Pielietosim TDD ciklu praktiskai, globāli saprotamai problēmai: naudas konvertēšanai starp valūtām. Mēs soli pa solim izveidosim `CurrencyConverter` moduli.

Iteration 1: Simple, Fixed-Rate Conversion

🔴 SARKANS: Uzrakstiet pirmo neizpildāmo testu

Mūsu pirmā prasība ir konvertēt konkrētu summu no vienas valūtas uz otru, izmantojot fiksētu likmi. Izveidojiet jaunu failu ar nosaukumu `CurrencyConverter.test.js`.

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

describe('CurrencyConverter', () => {
  it('should convert an amount from USD to EUR correctly', () => {
    // Arrange
    const amount = 10; // 10 USD
    const expected = 9.2; // Assuming a fixed rate of 1 USD = 0.92 EUR

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

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

Tagad palaidiet testu novērotāju no sava termināļa:

npm run test:watch

Tests iespaidīgi neizdosies. Jest ziņos kaut ko līdzīgu `TypeError: Cannot read properties of undefined (reading 'convert')`. Šis ir mūsu SARKANAIS stāvoklis. Tests neizdodas, jo `CurrencyConverter` neeksistē.

🟢 ZAĻŠ: Uzrakstiet vienkāršāko kodu, lai tests izdotos

Tagad panāksim, lai tests izdodas. Izveidojiet `CurrencyConverter.js`.

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

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

module.exports = CurrencyConverter;

Tiklīdz jūs saglabāsiet šo failu, Jest atkārtoti palaidīs testu, un tas kļūs ZAĻŠ. Mēs esam uzrakstījuši absolūti minimālo koda daudzumu, lai apmierinātu testa prasību.

🔵 REFAKTORĒT: Uzlabojiet kodu

Kods ir vienkāršs, bet mēs jau varam domāt par uzlabojumiem. Ligzdotais `rates` objekts ir nedaudz neelastīgs. Pagaidām tas ir pietiekami tīrs. Vissvarīgākais ir tas, ka mums ir strādājoša funkcionalitāte, ko aizsargā tests. Pāriesim pie nākamās prasības.

Iteration 2: Handling Unknown Currencies

🔴 SARKANS: Uzrakstiet testu nederīgai valūtai

Kas notiktu, ja mēs mēģinātu konvertēt uz valūtu, kuru mēs nezinām? Tam, visticamāk, vajadzētu izmest kļūdu. Definēsim šo uzvedību jaunā testā `CurrencyConverter.test.js`.

// In CurrencyConverter.test.js, inside the describe block

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

  // Act & Assert
  // We wrap the function call in an arrow function for Jest's toThrow to work.
  expect(() => {
    CurrencyConverter.convert(amount, 'USD', 'XYZ');
  }).toThrow('Unknown currency: XYZ');
});

Saglabājiet failu. Testu palaidējs nekavējoties parāda jaunu neveiksmi. Tas ir SARKANS, jo mūsu kods neizmet kļūdu; tas mēģina piekļūt `rates['USD']['XYZ']`, kas rezultējas `TypeError`. Mūsu jaunais tests ir pareizi identificējis šo trūkumu.

🟢 ZAĻŠ: Panāciet, lai jaunais tests izdodas

Modificēsim `CurrencyConverter.js`, lai pievienotu validāciju.

// 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]) {
      // Determine which currency is unknown for a better error message
      const unknownCurrency = !rates[from] ? from : to;
      throw new Error(`Unknown currency: ${unknownCurrency}`);
    }
    return amount * rates[from][to];
  }
};

module.exports = CurrencyConverter;

Saglabājiet failu. Abi testi tagad izdodas. Mēs esam atgriezušies ZAĻAJĀ stāvoklī.

🔵 REFAKTORĒT: Sakopiet to

Mūsu `convert` funkcija kļūst lielāka. Validācijas loģika ir sajaukta ar aprēķinu. Mēs varētu izcelt validāciju atsevišķā privātā funkcijā, lai uzlabotu lasāmību, bet pagaidām tas vēl ir pārvaldāms. Galvenais ir tas, ka mums ir brīvība veikt šīs izmaiņas, jo mūsu testi mums pateiks, ja mēs kaut ko salauzīsim.

Iteration 3: Asynchronous Rate Fetching

Hardcoding rates isn't realistic. Let's refactor our module to fetch rates from a (mocked) external API.

🔴 SARKANS: Uzrakstiet asinhronu testu, kas imitē API izsaukumu

Vispirms mums ir jāpārstrukturē mūsu konvertors. Tagad tam būs jābūt klasei, kuru mēs varam instancēt, iespējams, ar API klientu. Mums arī būs jāimitē `fetch` API. Jest to padara vieglu.

Pārrakstīsim mūsu testa failu, lai pielāgotos šai jaunajai, asinhronajai realitātei. Mēs sāksim, atkal testējot veiksmīgo scenāriju.

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

// Mock the external dependency
global.fetch = jest.fn();

beforeEach(() => {
  // Clear mock history before each test
  fetch.mockClear();
});

describe('CurrencyConverter', () => {
  it('should fetch rates and convert correctly', async () => {
    // Arrange
    // Mock the successful API response
    fetch.mockResolvedValueOnce({
      json: () => Promise.resolve({ rates: { EUR: 0.92 } })
    });

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

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

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

  // We'd also add tests for API failures, etc.
});

Šī koda palaišana radīs SARKANĀS krāsas jūru. Mūsu vecais `CurrencyConverter` nav klase, tam nav `async` metodes, un tas neizmanto `fetch`.

🟢 ZAĻŠ: Ieviesiet asinhrono loģiku

Tagad pārrakstīsim `CurrencyConverter.js`, lai tas atbilstu testa prasībām.

// 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}`);
    }

    // Simple rounding to avoid floating point issues in tests
    const convertedAmount = amount * rate;
    return parseFloat(convertedAmount.toFixed(2));
  }
}

module.exports = CurrencyConverter;

Kad jūs saglabāsiet, testam vajadzētu kļūt ZAĻAM. Ievērojiet, ka mēs arī pievienojām noapaļošanas loģiku, lai apstrādātu peldošā punkta neprecizitātes, kas ir izplatīta problēma finanšu aprēķinos.

🔵 REFAKTORĒT: Uzlabojiet asinhrono kodu

`convert` metode dara daudz: ielādē, apstrādā kļūdas, parsē un aprēķina. Mēs varētu to refaktorēt, izveidojot atsevišķu `RateFetcher` klasi, kas atbildētu tikai par API komunikāciju. Mūsu `CurrencyConverter` tad izmantotu šo ielādētāju. Tas atbilst Viena atbildības principam (Single Responsibility Principle) un padara abas klases vieglāk testējamas un uzturamas. TDD mūs virza uz šo tīrāko dizainu.

Common TDD Patterns and Anti-Patterns

Praktizējot TDD, jūs atklāsiet modeļus, kas darbojas labi, un antimodeļus, kas rada berzi.

Labi modeļi, kam sekot

Anti-Patterns to Avoid

TDD in the Broader Development Lifecycle

TDD nepastāv vakuumā. Tas lieliski integrējas ar modernām Agile un DevOps praksēm, īpaši globālām komandām.

Conclusion: Your Journey with TDD

Uz testiem balstīta izstrāde ir vairāk nekā testēšanas stratēģija—tā ir paradigmas maiņa mūsu pieejā programmatūras izstrādei. Tā veicina kvalitātes, pārliecības un sadarbības kultūru. Sarkans-Zaļš-Refaktorēt cikls nodrošina stabilu ritmu, kas ved jūs uz tīru, robustu un uzturamu kodu. Rezultātā iegūtais testu komplekts kļūst par drošības tīklu, kas aizsargā jūsu komandu no regresijām un par dzīvo dokumentāciju, kas palīdz iepazīties ar projektu jauniem komandas locekļiem.

Mācīšanās līkne var šķist stāva, un sākotnējais temps var likties lēnāks. Bet ilgtermiņa ieguvumi, samazinot atkļūdošanas laiku, uzlabojot programmatūras dizainu un palielinot izstrādātāju pārliecību, ir neizmērojami. Ceļš uz TDD apgūšanu ir disciplīnas un prakses ceļš.

Sāciet šodien. Izvēlieties vienu mazu, nekritisku funkcionalitāti savā nākamajā projektā un apņemieties sekot procesam. Vispirms uzrakstiet testu. Vērojiet, kā tas neizdodas. Panāciet, lai tas izdodas. Un tad, pats galvenais, refaktorējiet. Izbaudiet pārliecību, ko sniedz zaļš testu komplekts, un drīz jūs brīnīsieties, kā jebkad esat veidojis programmatūru citādi.