Eesti

Õppige, kuidas tõhusalt kasutada näidisfunktsioone oma testimisstrateegias, et luua robustset ja usaldusväärset tarkvara. See juhend käsitleb, millal, miks ja kuidas näidiseid praktiliste näidetega rakendada.

Näidisfunktsioonid: põhjalik juhend arendajatele

Tarkvaraarenduse maailmas on robustse ja usaldusväärse koodi kirjutamine ülimalt oluline. Põhjalik testimine on selle eesmärgi saavutamiseks ülioluline. Eriti ühiktestimine keskendub üksikute komponentide või funktsioonide isoleeritud testimisele. Kuid reaalsetes rakendustes on sageli keerulisi sõltuvusi, mis muudab üksuste täieliku isoleerimise keeruliseks. Siin tulevadki appi näidisfunktsioonid.

Mis on näidisfunktsioonid?

Näidisfunktsioon on reaalse funktsiooni simuleeritud versioon, mida saate oma testides kasutada. Selle asemel, et täita tegeliku funktsiooni loogikat, võimaldab näidisfunktsioon teil kontrollida selle käitumist, jälgida, kuidas seda välja kutsutakse, ja määratleda selle tagastusväärtusi. Nad on teatud tüüpi test-teisik.

Mõelge sellest nii: kujutage ette, et testite auto mootorit (testitav üksus). Mootor sõltub mitmesugustest muudest komponentidest, nagu kütuse sissepritsesüsteem ja jahutussüsteem. Selle asemel, et mootori testimise ajal käivitada tegelikke kütuse sissepritse- ja jahutussüsteeme, saate kasutada näidissüsteeme, mis simuleerivad nende käitumist. See võimaldab teil isoleerida mootori ja keskenduda spetsiifiliselt selle jõudlusele.

Näidisfunktsioonid on võimsad tööriistad:

Millal kasutada näidisfunktsioone

Mocks are most useful in these situations:

1. Väliste sõltuvustega üksuste isoleerimine

Kui teie testitav üksus sõltub välistest teenustest, andmebaasidest, API-dest või muudest komponentidest, võib reaalsete sõltuvuste kasutamine testimisel tekitada mitmeid probleeme:

Näide: Kujutage ette, et testite funktsiooni, mis hangib kasutajaandmeid kaug-API-st. Selle asemel, et testimise ajal teha tegelikke API-kutseid, saate kasutada näidisfunktsiooni API vastuse simuleerimiseks. See võimaldab teil testida funktsiooni loogikat, ilma et peaksite tuginema välise API saadavusele või jõudlusele. See on eriti oluline, kui API-l on päringulimiidid või iga päringuga seotud kulud.

2. Keeruliste interaktsioonide testimine

Mõnel juhul võib teie testitav üksus suhelda teiste komponentidega keerukatel viisidel. Näidisfunktsioonid võimaldavad teil neid interaktsioone jälgida ja kontrollida.

Näide: Mõelge funktsioonile, mis töötleb maksetehinguid. See funktsioon võib suhelda maksevärava, andmebaasi ja teavitusteenusega. Näidisfunktsioonide abil saate kontrollida, et funktsioon kutsub makseväravat õigete tehinguandmetega, uuendab andmebaasi tehingu staatusega ja saadab kasutajale teate.

3. Veaolukordade simuleerimine

Veakäsitluse testimine on teie rakenduse robustsuse tagamiseks ülioluline. Näidisfunktsioonid muudavad veaoolukordade simuleerimise lihtsaks, mida on reaalses keskkonnas raske või võimatu reprodutseerida.

Näide: Oletame, et testite funktsiooni, mis laadib faile pilvesalvestusteenusesse. Saate kasutada näidisfunktsiooni, et simuleerida võrguviga üleslaadimisprotsessi ajal. See võimaldab teil kontrollida, kas funktsioon käsitleb viga korrektselt, proovib uuesti üles laadida või teavitab kasutajat.

4. Asünkroonse koodi testimine

Asünkroonset koodi, näiteks koodi, mis kasutab tagasikutseid (callbacks), lubadusi (promises) või async/await, võib olla keeruline testida. Näidisfunktsioonid aitavad teil kontrollida asünkroonsete operatsioonide ajastust ja käitumist.

Näide: Kujutage ette, et testite funktsiooni, mis hangib andmeid serverist asünkroonse päringu abil. Saate kasutada näidisfunktsiooni serveri vastuse simuleerimiseks ja kontrollida, millal vastus tagastatakse. See võimaldab teil testida, kuidas funktsioon käsitleb erinevaid vastuse stsenaariume ja ajalõppe.

5. Soovimatute kõrvalmõjude vältimine

Mõnikord võib reaalse funktsiooni kutsumine testimise ajal põhjustada soovimatuid kõrvalmõjusid, näiteks andmebaasi muutmist, e-kirjade saatmist või väliste protsesside käivitamist. Näidisfunktsioonid hoiavad need kõrvalmõjud ära, võimaldades teil asendada reaalse funktsiooni kontrollitud simulatsiooniga.

Näide: Testite funktsiooni, mis saadab uutele kasutajatele tervitusmeile. Kasutades näidismeiliteenust, saate tagada, et meili saatmise funktsionaalsus ei saada teie testikomplekti käivitamise ajal tegelikult e-kirju reaalsetele kasutajatele. Selle asemel saate kontrollida, kas funktsioon üritab saata meili õige teabega.

Kuidas kasutada näidisfunktsioone

Konkreetsed sammud näidisfunktsioonide kasutamiseks sõltuvad teie kasutatavast programmeerimiskeelest ja testimisraamistikust. Kuid üldine protsess hõlmab tavaliselt järgmisi samme:

  1. Sõltuvuste tuvastamine: Tehke kindlaks, milliseid väliseid sõltuvusi peate näidistama.
  2. Näidisobjektide loomine: Looge näidisobjektid või -funktsioonid reaalsete sõltuvuste asendamiseks. Nendel näidistel on sageli omadused nagu `called`, `returnValue` ja `callArguments`.
  3. Näidise käitumise konfigureerimine: Määratlege näidisfunktsioonide käitumine, näiteks nende tagastusväärtused, veaoolukorrad ja väljakutsete arv.
  4. Näidiste süstimine: Asendage reaalsed sõltuvused näidisobjektidega oma testitavas üksuses. Seda tehakse sageli sõltuvuste süstimise (dependency injection) abil.
  5. Testi käivitamine: Käivitage oma test ja jälgige, kuidas testitav üksus suhtleb näidisfunktsioonidega.
  6. Interaktsioonide kontrollimine: Kontrollige, et näidisfunktsioone kutsuti oodatud argumentidega, tagastusväärtustega ja õige arv kordi.
  7. Algse funktsionaalsuse taastamine: Pärast testi taastage algne funktsionaalsus, eemaldades näidisobjektid ja pöördudes tagasi reaalsete sõltuvuste juurde. See aitab vältida kõrvalmõjusid teistele testidele.

Näidisfunktsioonide näited erinevates keeltes

Here are examples of using mock functions in popular programming languages and testing frameworks:

JavaScript koos Jestiga

Jest on populaarne JavaScripti testimisraamistik, mis pakub sisseehitatud tuge näidisfunktsioonidele.

// Function to test
function fetchData(callback) {
  setTimeout(() => {
    callback('Data from server');
  }, 100);
}

// Test case
test('fetchData calls callback with correct data', (done) => {
  const mockCallback = jest.fn();
  fetchData(mockCallback);

  setTimeout(() => {
    expect(mockCallback).toHaveBeenCalledWith('Data from server');
    done();
  }, 200);
});

Selles näites loob `jest.fn()` näidisfunktsiooni, mis asendab tegeliku tagasikutsefunktsiooni. Test kontrollib, et näidisfunktsiooni kutsutakse õigete andmetega, kasutades `toHaveBeenCalledWith()`.

Keerukam näide moodulitega:

// user.js
import { getUserDataFromAPI } from './api';

export async function displayUserName(userId) {
  const userData = await getUserDataFromAPI(userId);
  return userData.name;
}

// api.js
export async function getUserDataFromAPI(userId) {
  // Simulate API call
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({ id: userId, name: 'John Doe' });
    }, 50);
  });
}

// user.test.js
import { displayUserName } from './user';
import * as api from './api';

describe('displayUserName', () => {
  it('should display the user name', async () => {
    // Mock the getUserDataFromAPI function
    const mockGetUserData = jest.spyOn(api, 'getUserDataFromAPI');
    mockGetUserData.mockResolvedValue({ id: 123, name: 'Mocked Name' });

    const userName = await displayUserName(123);
    expect(userName).toBe('Mocked Name');

    // Restore the original function
    mockGetUserData.mockRestore();
  });
});

Siin kasutatakse `jest.spyOn`, et luua näidisfunktsioon `getUserDataFromAPI` funktsioonile, mis imporditakse `./api` moodulist. `mockResolvedValue` kasutatakse näidise tagastusväärtuse määramiseks. `mockRestore` on oluline tagamaks, et teised testid ei kasutaks tahtmatult näidistatud versiooni.

Python koos pytesti ja unittest.mock'iga

Python pakub mitmeid teeke näidistamiseks, sealhulgas `unittest.mock` (sisseehitatud) ja teegid nagu `pytest-mock` lihtsustatud kasutamiseks koos pytestiga.

# Function to test
def get_data_from_api(url):
    # In a real scenario, this would make an API call
    # For simplicity, we simulate an API call
    if url == "https://example.com/api":
        return {"data": "API data"}
    else:
        return None

def process_data(url):
    data = get_data_from_api(url)
    if data:
        return data["data"]
    else:
        return "No data found"

# Test case using unittest.mock
import unittest
from unittest.mock import patch

class TestProcessData(unittest.TestCase):
    @patch('__main__.get_data_from_api') # Replace get_data_from_api in the main module
    def test_process_data_success(self, mock_get_data_from_api):
        # Configure the mock
        mock_get_data_from_api.return_value = {"data": "Mocked data"}

        # Call the function being tested
        result = process_data("https://example.com/api")

        # Assert the result
        self.assertEqual(result, "Mocked data")
        mock_get_data_from_api.assert_called_once_with("https://example.com/api")

    @patch('__main__.get_data_from_api')
    def test_process_data_failure(self, mock_get_data_from_api):
        mock_get_data_from_api.return_value = None
        result = process_data("https://example.com/api")
        self.assertEqual(result, "No data found")

if __name__ == '__main__':
    unittest.main()

See näide kasutab `unittest.mock.patch`, et asendada `get_data_from_api` funktsioon näidisega. Test konfigureerib näidise tagastama konkreetset väärtust ja seejärel kontrollib, et `process_data` funktsioon tagastab oodatud tulemuse.

Siin on sama näide, kasutades `pytest-mock`'i:

# pytest version
import pytest

def get_data_from_api(url):
    # In a real scenario, this would make an API call
    # For simplicity, we simulate an API call
    if url == "https://example.com/api":
        return {"data": "API data"}
    else:
        return None

def process_data(url):
    data = get_data_from_api(url)
    if data:
        return data["data"]
    else:
        return "No data found"


def test_process_data_success(mocker):
    mocker.patch('__main__.get_data_from_api', return_value={"data": "Mocked data"})
    result = process_data("https://example.com/api")
    assert result == "Mocked data"


def test_process_data_failure(mocker):
    mocker.patch('__main__.get_data_from_api', return_value=None)
    result = process_data("https://example.com/api")
    assert result == "No data found"

`pytest-mock` teek pakub `mocker` fiksaatorit (fixture), mis lihtsustab näidiste loomist ja konfigureerimist pytesti testides.

Java koos Mockitoga

Mockito on populaarne näidistamisraamistik Java jaoks.

import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;

interface DataFetcher {
    String fetchData(String url);
}

class DataProcessor {
    private final DataFetcher dataFetcher;

    public DataProcessor(DataFetcher dataFetcher) {
        this.dataFetcher = dataFetcher;
    }

    public String processData(String url) {
        String data = dataFetcher.fetchData(url);
        if (data != null) {
            return "Processed: " + data;
        } else {
            return "No data";
        }
    }
}

public class DataProcessorTest {

    @Test
    public void testProcessDataSuccess() {
        // Create a mock DataFetcher
        DataFetcher mockDataFetcher = mock(DataFetcher.class);

        // Configure the mock
        when(mockDataFetcher.fetchData("https://example.com/api")).thenReturn("API Data");

        // Create the DataProcessor with the mock
        DataProcessor dataProcessor = new DataProcessor(mockDataFetcher);

        // Call the function being tested
        String result = dataProcessor.processData("https://example.com/api");

        // Assert the result
        assertEquals("Processed: API Data", result);

        // Verify that the mock was called
        verify(mockDataFetcher).fetchData("https://example.com/api");
    }

    @Test
    public void testProcessDataFailure() {
        DataFetcher mockDataFetcher = mock(DataFetcher.class);
        when(mockDataFetcher.fetchData("https://example.com/api")).thenReturn(null);

        DataProcessor dataProcessor = new DataProcessor(mockDataFetcher);
        String result = dataProcessor.processData("https://example.com/api");
        assertEquals("No data", result);
        verify(mockDataFetcher).fetchData("https://example.com/api");
    }
}

Selles näites loob `Mockito.mock()` näidisobjekti `DataFetcher` liidese jaoks. `when()` kasutatakse näidise tagastusväärtuse konfigureerimiseks ja `verify()` kasutatakse kontrollimiseks, et näidist kutsuti oodatud argumentidega.

Parimad praktikad näidisfunktsioonide kasutamisel

Alternatiivid näidisfunktsioonidele

Kuigi näidisfunktsioonid on võimas tööriist, ei ole need alati parim lahendus. Mõnel juhul võivad muud tehnikad olla sobivamad:

Kokkuvõte

Näidisfunktsioonid on oluline tööriist tõhusate ühiktestide kirjutamiseks, võimaldades teil isoleerida üksusi, kontrollida käitumist, simuleerida veaoolukordi ja testida asünkroonset koodi. Järgides parimaid praktikaid ja mõistes alternatiive, saate kasutada näidisfunktsioone robustsema, usaldusväärsema ja hooldatavama tarkvara ehitamiseks. Pidage meeles kaaluda kompromisse ja valida igas olukorras õige testimistehnika, et luua terviklik ja tõhus testimisstrateegia, olenemata sellest, millises maailma osas te arendate.