Naar inhoud
lightbulb Welkom op de nieuwe kennisbank | We hebben de docs volledig vernieuwd met meer dan 160 features. Bekijk wat nieuw isarrow_forward

Unit tests voor Apps Script schrijven

Schrijf geautomatiseerde tests voor Apps Script met eigen assert-helpers, een lichtgewicht library zoals GasT, of Jest via clasp voor lokale uitvoering.

Teststrategieen voor Apps Script

Apps Script heeft geen ingebouwd testframework zoals Jest of PHPUnit. Je hebt grofweg drie opties:

  1. Zelf schrijven: eenvoudige assert-functies in Apps Script.
  2. Lichtgewicht library: een testframework dat speciaal voor Apps Script is gemaakt, zoals GasT, GASUnit of UnitTestingApp.
  3. Jest via clasp: schrijf tests in TypeScript of JavaScript die lokaal op je eigen machine draaien.

Voor de meeste projecten zijn optie 1 of 2 het meest pragmatisch. Optie 3 loont zodra je veel pure businesslogica hebt die je los van de Google-services wilt testen.

info

Wat draait waar?

Eigen helpers en libraries als GasT draaien binnen de Apps Script-omgeving, dus ze hebben toegang tot echte services zoals SpreadsheetApp. Jest draait op je eigen computer (Node.js) en kent die services niet, dus daar moet je ze mocken.

Eigen testhelpers schrijven

Een handvol assert-functies is genoeg om serieus te kunnen testen. Plak deze in een apart bestand, bijvoorbeeld Test.gs.

function assert(conditie, bericht) {
  if (!conditie) {
    throw new Error(`TEST MISLUKT: ${bericht}`);
  }
}

function assertEqual(werkelijk, verwacht, bericht = '') {
  if (werkelijk !== verwacht) {
    throw new Error(`TEST MISLUKT ${bericht}: verwacht "${verwacht}", kreeg "${werkelijk}"`);
  }
}

function assertBevatSleutel(obj, sleutel, bericht = '') {
  if (!(sleutel in obj)) {
    throw new Error(`TEST MISLUKT ${bericht}: sleutel "${sleutel}" ontbreekt`);
  }
}

function assertGooit(fn, bericht = '') {
  try {
    fn();
    throw new Error(`TEST MISLUKT ${bericht}: verwachtte een fout, maar geen fout gegooid`);
  } catch (e) {
    if (e.message.startsWith('TEST MISLUKT')) throw e;
  }
}

Tests schrijven voor bedrijfslogica

De grootste winst zit in het testen van pure functies: berekeningen, validaties en transformaties zonder service-aanroepen.

function berekenKorting(bedrag, klantType) {
  if (bedrag <= 0) throw new Error('Bedrag moet positief zijn');
  switch (klantType) {
    case 'PREMIUM': return bedrag * 0.15;
    case 'STANDAARD': return bedrag * 0.05;
    case 'NIEUW': return 0;
    default: throw new Error(`Onbekend klanttype: ${klantType}`);
  }
}

function runBerekenKortingTests() {
  assertEqual(berekenKorting(100, 'PREMIUM'), 15, 'Premium 15%');
  assertEqual(berekenKorting(200, 'STANDAARD'), 10, 'Standaard 5%');
  assertEqual(berekenKorting(50, 'NIEUW'), 0, 'Nieuw geen korting');
  assertGooit(() => berekenKorting(-10, 'PREMIUM'), 'Negatief bedrag');
  assertGooit(() => berekenKorting(100, 'ONBEKEND'), 'Onbekend type');
  Logger.log('berekenKorting: alle tests geslaagd');
}
lightbulb

Naamgeving maakt vinden makkelijk

Begin testfuncties consequent met test of run, zoals runBerekenKortingTests. Zo herken je ze meteen in de functiekiezer van de editor en kun je losse suites snel handmatig draaien.

Testrunner met rapportage

Met een eenvoudige runner draai je alle suites in een keer en krijg je een overzichtelijk resultaat in het logboek.

function runAllTests() {
  const suites = [
    {naam: 'berekenKorting', fn: runBerekenKortingTests},
    {naam: 'formateerDatum', fn: runFormateerDatumTests},
    {naam: 'valideerEmail', fn: runValideerEmailTests},
  ];

  let geslaagd = 0;
  let mislukt = 0;
  const rapport = [];

  suites.forEach(suite => {
    try {
      suite.fn();
      geslaagd++;
      rapport.push(`GESLAAGD: ${suite.naam}`);
    } catch (e) {
      mislukt++;
      rapport.push(`MISLUKT: ${suite.naam} - ${e.message}`);
    }
  });

  rapport.forEach(r => Logger.log(r));
  Logger.log(`Resultaat: ${geslaagd}/${geslaagd + mislukt} geslaagd`);

  if (mislukt > 0) {
    throw new Error(`${mislukt} testsuites mislukt`);
  }
}

Mocking van services

Voor functies die GmailApp, SpreadsheetApp of een andere service aanroepen, geef je een nep-versie (mock) mee in plaats van de echte service. Zo test je het gedrag zonder echt e-mails te versturen.

let mockEmailVerzonden = false;
let mockEmailData = null;

const MockGmailApp = {
  sendEmail: (naar, onderwerp, body) => {
    mockEmailVerzonden = true;
    mockEmailData = {naar, onderwerp, body};
  },
};

function notificatieService(emailService, ontvanger, bericht) {
  if (!ontvanger || !bericht) {
    throw new Error('Ontvanger en bericht zijn verplicht');
  }
  emailService.sendEmail(ontvanger, 'Notificatie', bericht);
}

function runNotificatieTests() {
  mockEmailVerzonden = false;
  notificatieService(MockGmailApp, 'test@voorbeeld.nl', 'Test bericht');
  assert(mockEmailVerzonden, 'E-mail zou verstuurd moeten zijn');
  assertEqual(mockEmailData.naar, 'test@voorbeeld.nl', 'Ontvanger');

  assertGooit(
    () => notificatieService(MockGmailApp, '', 'bericht'),
    'Lege ontvanger'
  );

  Logger.log('notificatieService: alle tests geslaagd');
}

De truc is dat notificatieService de service als parameter ontvangt in plaats van GmailApp rechtstreeks aan te roepen. Die zogenoemde dependency injection maakt je code testbaar.

Integratietests voor Sheets

Soms wil je toch tegen de echte service testen, bijvoorbeeld om te controleren of data correct wordt weggeschreven. Ruim na afloop altijd op met een try/finally-blok.

function runSheetIntegratieTest() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  let testBlad = ss.getSheetByName('__TEST__');

  if (testBlad) ss.deleteSheet(testBlad);
  testBlad = ss.insertSheet('__TEST__');

  try {
    testBlad.getRange('A1:C3').setValues([
      ['Naam', 'Score', 'Actief'],
      ['Anna', 95, true],
      ['Bob', 72, false],
    ]);

    const data = testBlad.getDataRange().getValues();
    assertEqual(data.length, 3, 'Aantal rijen');
    assertEqual(data[1][0], 'Anna', 'Eerste naam');
    assertEqual(data[1][1], 95, 'Anna score');

    Logger.log('Sheet integratie: geslaagd');
  } finally {
    ss.deleteSheet(testBlad);
  }
}
warning

Test nooit op een productie-spreadsheet

Een integratietest die bladen aanmaakt en verwijdert hoort thuis in een aparte testkopie, niet in een spreadsheet waar je collega's mee werken. Een mislukte test kan anders echte data raken.

Lokaal testen met clasp en Jest

Wil je pure logica buiten de editor testen, dan haal je je code met clasp naar je eigen machine. Clasp is de officiele command-line tool van Google en vereist Node.js v20 of nieuwer (stand juni 2026).

Jest opzetten via clasp

  1. Installeer clasp met npm install -g @google/clasp en log in met clasp login.
  2. Haal je project op met clasp clone <scriptId> of start nieuw met clasp create.
  3. Installeer Jest als dev-dependency: npm install --save-dev jest.
  4. Zet je businesslogica in losse modules en schrijf daar *.test.js-bestanden bij.
  5. Voeg testbestanden toe aan .claspignore zodat ze niet mee-gepusht worden naar Apps Script.
  6. Draai npx jest lokaal; pushen doe je daarna met clasp push.

Voor service-interacties (SpreadsheetApp, GmailApp) blijft mocking of een integratietest in de editor nodig, omdat die services lokaal niet bestaan.

Kan ik Jest gebruiken met Apps Script?

Via clasp kun je Jest lokaal draaien voor pure logica. Services zoals SpreadsheetApp moet je mocken. Dit werkt goed voor businesslogica, maar niet voor echte service-interacties; die test je in de editor.

Welke testlibraries bestaan er specifiek voor Apps Script?

Bekende opties zijn GasT (TAP-stijl), GASUnit en UnitTestingApp. Ze draaien binnen de Apps Script-omgeving en hebben dus toegang tot de echte services. Voor kleine projecten volstaan vaak je eigen assert-helpers.

Hoe run ik tests automatisch bij elke push?

Voeg een CI-workflow toe, bijvoorbeeld GitHub Actions, die je Jest-tests draait en daarna met clasp pusht. Voor het uitvoeren van testfuncties in de cloud via clasp run heb je een API-deployment en autorisatie nodig, bijvoorbeeld met een service-account.

Moet ik elke Apps Script functie testen?

Test de businesslogica zoals berekeningen, validaties en transformaties grondig. Service-aanroepen test je gerichter via mocking of een enkele integratietest, niet uitputtend.

Hoe herstel ik testdata als een test halverwege mislukt?

Gebruik een try/finally-blok in je integratietest, zodat testbladen en testdata altijd worden opgeruimd, ook bij een fout halverwege.

Werkt mijn code lokaal anders dan in Apps Script?

Soms wel. Apps Script draait op een V8-runtime, maar globale objecten en services verschillen van Node.js. Houd je logica daarom service-onafhankelijk en injecteer services als parameter.

Test-driven development in de praktijk

Zo werk je test-eerst

  1. Schrijf de assert-helpers en testrunner als eerste op.
  2. Schrijf een falende test voor de nieuwe functie.
  3. Implementeer de minimale code om die test te laten slagen.
  4. Refactor de code terwijl de tests groen blijven.
  5. Voeg randgevallen toe als extra tests.
  6. Draai runAllTests() voor elke deploy.

Tests zijn de vangrail bij het wijzigen van Apps Script code. Zelfs eenvoudige assert-functies voorkomen regressies en geven vertrouwen wanneer je productiescripts aanpast.