1. Johdanto
Testiautomaatio on olennainen osa modernin ohjelmistokehitysprosessin laadunvarmistusta. Mahdollisimman sujuvan tuotekehityksen ja laadunvarmistuksen mahdollistamiseksi on erittäin tärkeää kyetä valitsemaan tuotekehityksen kannalta oikeat työkalut ja lähestymistavat, jotta tuotteiden julkaisukelpoisuus saavutetaan nopeasti ja luotettavasti. Tässä artikkelissa pureudumme testiautomaation kehittämiseen ohjelmiston toiminnan kannalta keskeisissä kerroksissa: API-rajapinnoissa ja käyttöliittymässä, eli UI:ssa.
Molemmat lähestymistavat ovat keskeisiä, mutta palvelevat hiukan eri tarkoituksia. API-testaus keskittyy sovelluksen ”selkärankaan”, eli palvelimien, tietokantojen ja muiden taustajärjestelmien toimintaan. UI-testaus puolestaan testaa järjestelmien toimintaa käyttöliittymän kautta, varmistaen samalla UI-elementtien toimivuuden ja datan esittämisen käyttöliittymässä oikeanlaisesti.
API-testaus tarjoaa nopeutta, skaalautuvuutta ja tarkkuutta. Sillä voidaan testata sovelluksen ydinlogiikkaa jo ennen kuin käyttöliittymä olisi vielä testattavissa. Toisaalta UI-testaus mahdollistaa sovelluksen näkyvän kerroksen tarkistamisen juuri sellaisena, kuin loppukäyttäjä sen kokee. Näillä kahdella lähestymistavalla on selkeästi eri roolit, mutta yhdessä ne mahdollistavat luomaan teknisen perustan ohjelmiston testiautomaatiostrategialle ja sen myötä kehitettävälle kehykselle.
Tässä artikkelissa vertailemme näiden kahden lähestymistavan etuja, haasteita ja käyttötapauksia. Tarkastelemme myös sitä, miten ohjelmoitavien rajapintojen (API) ja käyttöliittymän (UI) kautta tapahtuva testaus täydentävät toisiaan erityisesti end-to-end testauksessa, ja miten ne voivat toimia osana laajempaa testausstrategiaa. Lähdetään tutkimaan, mitä testaamisen kerrokset voivat tarjota sovellusten laadunvarmistukselle!
2. Playwright
Artikkelin esimerkeissä tarkastelemme Playwrightia, Microsoftin kehittämää avoimen lähdekoodin työkalua testiautomaatioon. Playwright tukee kaikkia merkittävimpiä selaimia ja mahdollistaa sekä käyttöliittymän että ohjelmoitavien rajapintojen testaamisen samalla työkalulla. Sen monipuoliset ominaisuudet ja sisäänrakennettu tuki monisäikeiselle suorittamiselle tekevät siitä käyttökelpoisen valinnan monimutkaisten web-sovellusten testaamiseen.
Testattavana tuotteena käytämme tätä Herokuapp:in tarjoamaa testaussivua, joka soveltuu yksinkertaisen esimerkkimme tarpeisiin mainiosti. Esimerkeissä käymme läpi tyypillisiä end-to-end -testauksen testitapauksia.
3. API-testaus: Sovelluksen selkäranka
Tässä esimerkissä keskitymme tyypilliseen web-sovelluksen kirjautumistoimintoon, joka validoidaan API-testillä, että käyttäjä saa oikeanlaisen vastauksen palvelimelta:
// Language: TypeScript
// Test site: https://thinking-tester-contact-list.herokuapp.com/
import { test, expect } from '@playwright/test';
import { user } from '../data/data';
const testUser = user
test('Login and logout via API', async ({ request }) => {
// 1. Send POST request to login endpoint
const response = await request.post('https://thinking-tester-contact-list.herokuapp.com/users/login', {
data: {
email: testUser.email,
password: testUser.password,
},
headers: {
'Content-Type': 'application/json', // Explicitly set the content type
},
});
// 2. Verify response status
expect(response.status()).toBe(200);
// 3. Parse and validate the response body
const responseBody = await response.json();
// 4. Save token for later use
const token = responseBody.token;
// 5. Log out using the token
const logoutResponse = await request.post('https://thinking-tester-contact-list.herokuapp.com/users/logout', {
headers: {
Authorization: `Bearer ${token}`,
},
});
// 6. Validate logout response
expect(logoutResponse.status()).toBe(200);
});
Esimerkin kirjautumistestissä palvelimen palauttamaa tokenia voitaisiin käyttää jatkossa muissakin API-testeissä varmistaen, että käyttäjällä on oikeudet suorittaa muita API-kutsuja.
4. UI-testaus: Loppukäyttäjän näkymä
Käyttöliittymätestaus simuloi käyttäjän vuorovaikutusta sovelluksen kanssa. Tämä on tärkeää sen varmistamiseksi, että sovelluksen käyttöliittymä toimii odotetusti ja odotetulla datalla. Alla esimerkki siitä, miten web-sovelluksen kirjautumisen voi testata käyttöliittymän kautta:
// Language: TypeScript
// Test site: https://thinking-tester-contact-list.herokuapp.com/
import { test, expect } from '@playwright/test';
import { user } from '../data/data';
const testUser = user
test('Login and logout via UI', async ({ page }) => {
// 1. Navigate to the login page
await page.goto('https://thinking-tester-contact-list.herokuapp.com/');
// 2. Fill in the email and password fields
await page.fill('#email', testUser.email);
await page.fill('#password', testUser.password);
// 3. Click the Submit button
await page.click('#submit');
//4. Verify the login was successful
await expect(page.locator('H1')).toHaveText("Contact List");
//5. Logout
await page.click('#logout');
await expect(page.locator('H1')).toHaveText("Contact List App");
});
5. API- ja UI-testaus: Vertailu
Vaikka API- ja UI-testaus ovat erilaisia lähestymistapoja ohjelmistoon, ne eivät ole toisiaan poissulkevia. API-testaus on nopeampaa ja vakaampaa, kun taas UI-testaus on tärkeää käyttöliittymän toiminnallisuuden ja siellä esitettävän datan varmistamisessa.
Ominaisuus | API-testaus | UI-testaus |
Nopeus | Nopeaa, ei visuaalisia odotuksia | Hitaampaa, koska edellyttää selainta ja polun suorittaa toimintoja käyttäjän näkökulmasta. |
Luotettavuus | Vähemmän häiriöaltis | Alttiimpi käyttöliittymävirheille |
Käyttötapaukset | Palvelinlogiikan testaus | Käyttöliittymän testaus |
Riippuvuudet | Ei edellytä valmista käyttöliittymää | Vaatii täysin toimivan käyttöliittymän, ja edellyttää API:a enemmän testattavuutta. |
6. Yhdistämällä API- ja UI-testaus
Jakamalla testauskattavuutta API- ja UI-tasojen kesken, saavutetaan erinomaiset lähtökohdat toimivalle ja hyvin ylläpidettävälle testiautomaatiolle. Kiteytettynä se tarkoittaa, että testaus ei rajoitu vain sovelluksen käyttöliittymään tai taustajärjestelmiin, vaan molempia testataan rinnakkain varmistamaan, että ne toimivat saumattomasti yhdessä. Täsmennettäköön vielä, että tässä artikkelissa käsitellään pääasiassa end-to-end -tyyppisen testauksen rakentamista.

Tyypillisesti API-testaus on konkreettisemmin läsnä aiemmissa testausvaiheissa, kuten moduli- ja integraatiotestauksessa. End-to-end testauksen tapauksessa pitäisikin puhua mieluummin ohjelmoitavien rajapintojen kautta testaamisesta, koska tarkoitus ei ole niinkään testata itse ohjelmoitavan rajapinnan, tai vättämättä edes suoraan kyseisen rajapinnan tarjoman alijärjestelmän toimintaa, vaan hyödyntää kyseistä rajapintaa korkeamman tason testeissä. Käytämme kuitenkin tässä yhteydessä yksinkertaisuuden vuoksi termiä API-testaus, myös tästä ohjelmoitavien rajapintojen kautta tapahtuvasta testauksesta.
Suosituin syy rakentaa testejä API-rajapintojen kautta, on yksiselitteisesti menetelmän nopeus ja luotettavuus. Ohjelmoitavien rajapintojen parissa toimiessa, kutsut ja vastaukset siirtyvät suoraviivaisesti testijärjestelmän ja testattavan taustajärjestelmän (backend) välillä, eikä testiautomaation tarvitse operoida käyttöliittymäkomponenttien (frontend) parissa, tai huomioida käyttöliittymän, kuten web-sivun, latausaikoja. Siksi monella testiautomaatiokehittäjällä on tahtotila kirjoittaa testit API-tasolla niin pitkälle kuin tuotteen testauksen kannalta on järkevää.
Tässä esimerkissä API- ja UI- testauksen yhdistelmä on toteutettu seuraavasti:
- Kirjautuminen testataan ensin API-testillä – varmistaen, että järjestelmä hyväksyy oikeat tunnukset ja hylkää virheelliset.
- Jos kirjautuminen onnistuu, siirrytään siirrytään testin UI:lla toteutettavaan osaan.
- Tarkistetaan, että käyttäjä näkee kirjautumisen jälkeen oikean näkymän.
- Lisätään uusi asiakastieto käyttöliittymän kautta – tämä varmistaa, että lomakkeet ja syöttökentät toimivat oikein, sekä että tiedot tallentuvat järjestelmään oikein.
- Tarkistetaan, että uusi asiakastieto näkyy käyttöliittymässä oikein – varmistaen, että loppukäyttäjä näkee päivitetyt tiedot.
- Poistetaan asiakastietojärjestelmästä ja varmistetaan, ettei sitä enää näy käyttöliittymässä.
Tällainen testausstrategia varmistaa, että sekä sovelluksen sisäiset toiminnot että käyttäjäkokemus ovat kunnossa. API-testit takaavat nopeuden ja tarkkuuden, kun taas UI-testit varmistavat, että loppukäyttäjän näkymä toimii odotetusti. Pelkästään sisäänkirjautumisen suorittaminen voi säästää testien ajoaikaa useita sekunteja testiä kohden.
// Language: TypeScript
// Test site: https://thinking-tester-contact-list.herokuapp.com/
import { test, expect } from '@playwright/test';
import { getNewContactData, user } from '../data/data';
const contactData = getNewContactData();
const testUser = user;
test('Login via API then Add and remove contact via UI', async ({ request, page }) => {
// 1. Send POST request to login endpoint
const response = await request.post('https://thinking-tester-contact-list.herokuapp.com/users/login', {
data: {
email: testUser.email,
password: testUser.password,
},
headers: {
'Content-Type': 'application/json',
},
});
// 2. Verify response status
expect(response.status()).toBe(200);
// 3. Parse and validate the response body
const responseBody = await response.json();
// 4. Set the token to page with extraHTTPHeaders
await page.setExtraHTTPHeaders({
'Authorization': `Bearer ${responseBody.token}`
});
// 5. After we have logged in, we go to the contact list page and add a new contact
await page.goto('https://thinking-tester-contact-list.herokuapp.com/contactList');
await expect(page.locator('H1')).toHaveText("Contact List");
// 6. Add new contact
await page.click('button#add-contact');
// 7. Fill in the contact form fields
await page.fill('#firstName', contactData.firstName);
await page.fill('#lastName', contactData.lastName);
await page.fill('#birthdate', contactData.birthdate);
await page.fill('#email', contactData.email);
await page.fill('#phone', contactData.phone);
await page.fill('#street1', contactData.street1);
await page.fill('#street2', contactData.street2);
await page.fill('#city', contactData.city);
await page.fill('#stateProvince', contactData.stateProvince);
await page.fill('#postalCode', contactData.postalCode);
await page.fill('#country', contactData.country);
// 8. Submit the form
await page.click('#submit');
// 9. Verify the contact was added successfully
await expect(page.locator('H1')).toHaveText("Contact List");
// 10. Select the contact and delete it
await page.click(`text=${contactData.firstName} ${contactData.lastName}`);
await page.waitForSelector(`#lastName:has-text("${contactData.lastName}")`);
// 11. Click "ok" in confirmation dialog
await page.on('dialog', async dialog => {
await dialog.accept();
console.log('Dialog accepted');
});
await page.click('button#delete');
// 12. Verify the contact was deleted successfully
await page.waitForSelector('H1:has-text("Contact List")');
await expect(page.locator(`td:has-text("${contactData.firstName} ${contactData.lastName}")`)).not.toBeVisible();
});
Huom. Useimmat testiautomaatioframeworkit mahdollistavat autentikoidun tilan tallentamisen ja uudelleenkäytön testeissä. Tämä vähentää tarvetta suorittaa kirjautuminen jokaisessa testissä erikseen. Mikäli suoritettavilla testeillä ei ole riippuvuuksia voidaan edellä mainittua käyttää käyttää nopeuttamaan testejä.
7. Yhteenveto
API- ja UI-testaus eivät ole toisiaan poissulkevia, vaan ne täydentävät toisiaan osana tehokasta testausstrategiaa. API-testaus on ensisijainen ja suositeltava tapa varmistaa sovelluksen ydinlogiikan ja taustajärjestelmien toiminta, sillä se on nopeampi, luotettavampi ja vähemmän altis häiriöille kuin UI-testit. UI-testauksen rooli tulisi rajata ainoastaan niihin kohtiin, joissa käyttöliittymän toiminta on kriittistä sovelluksen laadun varmistamiseksi.
Paras testausstrategia hyödyntää API-testauksen vahvuuksia mahdollisimman laajasti ja käyttää UI-testausta vain silloin, kun se on välttämätöntä. Tämä lähestymistapa varmistaa sekä teknisen luotettavuuden että tehokkuuden, mikä vähentää testauksen ylläpitokustannuksia ja nopeuttaa testiajoja. Näin saavutetaan korkea ohjelmiston laatu ilman tarpeetonta riippuvuutta hitaammista ja haavoittuvammista UI-testeistä.
Lue lisää testiautomaatiokehityksen palveluistamme >>
Tutustu myös testiautomaatioon liittyviin asiakasprojektikuvauksiimme: