Zoeken…


Opmerkingen

Nightwatch levert al sinds v0,5 dagen acceptatie- en end-to-end-testen voor Meteor-apps en heeft migraties van PHP naar Spark naar Blaze en React beheerd; en alle belangrijke platforms voor continue integratie. Zie voor meer hulp:

Nightwatch API-documentatie
Nightwatch.js Google Group

App-oppervlakte

Op het meest basale niveau zijn acceptatietests in wezen black-box testen, die fundamenteel bezig zijn met het testen van inputs en outputs van een gesloten systeem. Als zodanig zijn er drie essentiële kenmerken voor acceptatietests: een bron zoeken, gegevens lezen en gegevens schrijven. Als het gaat om browsers en webapps, komen deze drie functies in principe neer op het volgende:

  1. Laad een webpagina of applicatieweergave
  2. Inspecteer gebruikersinterface-elementen (bijv. DOM)
  3. Een gebeurtenis activeren / een gebruikersinteractie simuleren

We noemen dit het oppervlak van de applicatie. Oppervlakte is alles wat een gebruiker ziet of ervaart. Het is de buitenkant van een blackbox-systeem. En aangezien gebruikers interactie hebben met moderne webapplicaties op videoschermen met behulp van webbrowsers, wordt onze dekking van het oppervlak gedefinieerd door universele resource locators (URL's) en viewports. En dus begint onze allereerste walkthrough er ongeveer zo uit te zien:

module.exports = {
  "Hello World" : function (client) {
    client
      // the location of our Meteor app
      .url("http://localhost:3000")

      // the size of the viewport 
      .resizeWindow(1024, 768)

      // test app output
      .verify.elementPresent('h1')
      .verify.containsText('h1', "Welcome to Meteor!")
      .verify.containsText('p', "You've pressed the button 0 times")
      .verify.elementPresent('button')

      // simulate user input
      .click('button').pause(500)

      // test app output again, to make sure input worked
      .verify.containsText('p', "button 1 times")

      // saving a copy of our viewport pixel grid
      .saveScreenshot('tests/nightwatch/screenshots/homepage.png')
      .end();
  }
};

Aangepaste opdrachten

Nightwatch ondersteunt het maken van aangepaste opdrachten die toetsaanslagen, muisklikken en andere invoer kunnen simuleren. Een aangepast commando kan worden gekoppeld aan andere Nightwatch-commando's, zoals:

module.exports = {
  "Login App" : function (client) {
    client
      .url("http://localhost:3000")
      .login("[email protected]", "janedoe123")
      .end();
  }
};

Om dit in te schakelen, definieert u een opdracht in ./tests/nightwatch/commands/login als volgt:

exports.command = function(username, password) {

  this
    .verify.elementPresent('#login')

      // we clear the input in case there's any data remaining from previous visits
      .clearValue("#emailInput")
      .clearValue("#passwordInput")

      // we simulate key presses
      .setValue("#emailInput", username)
      .setValue("#passwordInput", password)

    // and we simulate a mouse click
    .click("#signInToAppButton").pause(1000)

  return this; // allows the command to be chained.
};

Om dit allemaal te laten werken, moet u id kenmerken toevoegen aan uw inlogpagina. Op een bepaald niveau moet het er ongeveer zo uitzien:

<template name="login">
  <div id="login">
    <input id="emailInput" name="email" type="email" />
    <input id="passwordInput" name="password" type="password" />
    <button id="#signInToAppButton">Sign In</button>
  </div>
</template>

Meteor-objecten op de client inspecteren

Omdat Nightwatch toegang heeft tot de browserconsole, is het mogelijk om client-side objecten te inspecteren met behulp van de .execute() API. In het volgende voorbeeld controleren we het sessieobject voor een bepaalde sessievariabele. Eerst beginnen we met het maken van het bestand ./tests/nightwatch/api/meteor/checkSession , waar we het volgende commando behouden:

// syncrhonous version; only works for checking javascript objects on client
exports.command = function(sessionVarName, expectedValue) {
  var client = this;
  this
    .execute(function(data){
      return Session.get(data);
    }, [sessionVarName], function(result){
      client.assert.ok(result.value);
      if(expectedValue){
        client.assert.equal(result.value, expectedValue);
      }
    })
    return this;
};

We kunnen het dan als volgt ketenen:

module.exports = {
  "Check Client Session" : function (client) {
    client
      .url("http://localhost:3000")
      .checkSession("currentUser", "Jane Doe")
      .end();
  }
};

Formulieren & invoertypen

Om een bestand te uploaden, moet u eerst een / data-map maken en het bestand toevoegen dat u wilt uploaden.

tests/nightwatch/data/IM-0001-1001.dcm

Uw formulier heeft invoer nodig met het type bestand. (Sommige mensen houden niet van de stijlopties die deze invoer biedt; en een gebruikelijk patroon is om deze invoer verborgen te maken; en om een andere knop op de pagina te laten klikken namens de gebruiker.)

<form id="myform">
    <input type="file" id="fileUpload">
    <input type="text" name="first_name">
    <input type="text" name="last_name">

    <input type="date" name="dob_month">
    <input type="date" name="dob_day">
    <input type="date" name="dob_year">

    <input type="radio" name="gender" value="M">
    <input type="radio" name="gender" value="F">
    <input type="radio" name="gender" value="O">

    <input type="select" name="hs_graduation_year">
    <input type="text" name="city">
    <input type="select" name="state">

    <input type="submit" name="submit" value="Submit">
</form>

Uw tests moeten dan setValue () gebruiken en het pad naar het lokale bestandsitem oplossen.

module.exports = {
  "Upload Study" : function (client) {
    console.log(require('path').resolve(__dirname +  '/../data' ));

    var stringArray = "Chicago";

    client
      .url(client.globals.url)
      .verify.elementPresent("form#myform")

      // input[type="file"]
      .verify.elementPresent("input#fileUpload")
      .setValue('input#fileUpload', require('path').resolve(__dirname + '/../data/IM-0001-1001.dcm'))

      // input[type="text"]
      .setValue('input[name="first_name"]', 'First')
      .setValue('input[name="last_name"]', 'Last')

      // input[type="date"]
      .click('select[name="dob_month"] option[value="3"]')
      .click('select[name="dob_day"] option[value="18"]')
      .click('select[name="dob_year"] option[value="1987"]')

      // input[type="radio"]
      .click('input[name="gender"][value="M"]')

      // input[type="number"]
      .click('select[name="hs_graduation_year"] option[value="2002"]')

      // input[type="text"]
      // sometimes Nightwatch will send text faster than the browser can handle
      // which will cause skipping of letters.  In such cases, we need to slow
      // Nightwatch down; which we do by splitting our input into an array
      // and adding short 50ms pauses between each letter
      for(var i=0; i < userIdArray.length; i++) {
        client.setValue('input[name="city"]', stringArray[i]).pause(50)
      }

      // input[type="select"]
      // after an array input above, we need to resume our method chain...
      client.click('select[name="state"] option[value="CA"]')

      // input[type="number"]
      .setValue('input[name="zip"]', '01234')

      //input [ type="submit" ]
      .click('button[type="submit"]')
      .end();
  }
};

Met dank aan Daniel Rinehart voor het inspireren van dit voorbeeld.

Componenten & pagina-objecten

Pagina-objecten zijn vergelijkbaar met aangepaste opdrachten; behalve dat het verzamelingen van aangepaste opdrachten zijn die aan een specifieke UI-component zijn gekoppeld. Dit werkt buitengewoon goed met modern ontwerp op basis van componenten, zoals in React.

module.exports = {
  url: 'http://localhost:3000/login',
  commands: [{
  login: function(email, password) {
    return this
      .clearValue('input[name="emailAddress"]')
      .clearValue('input[name="password"]')

      .setValue('input[name="emailAddress"]', email)
      .setValue('input[name="password"]', password)

      .verify.elementPresent('#loginButton')
      .click("#loginButton");
  },
  clear: function() {
    return this
      .waitForElementVisible('@emailInput')
      .clearValue('@emailInput')
      .clearValue('@passInput')
      .waitForElementVisible('@loginButton')
      .click('@loginButton')
  },
  checkElementsRendered: function(){
    return this
      .verify.elementPresent("#loginPage")
      .verify.elementPresent('input[name="emailAddress"]')
      .verify.elementPresent('input[name="password"]')
  },
  pause: function(time, client) {
    client.pause(time);
    return this;
  },
  saveScreenshot: function(path, client){
    client.saveScreenshot(path);
    return this;
  }
}],
  elements: {
    emailInput: {
      selector: 'input[name=email]'
    },
    passInput: {
      selector: 'input[name=password]'
    },
    loginButton: {
      selector: 'button[type=submit]'
    }
  }
};

Het enige voorbehoud bij het gebruik van het PageObject-patroon bij het testen van componenten, is dat de implementatie de methodeketenstroom verbreekt die de native Nightwatch verify.elementPresent biedt. In plaats daarvan moet u het pagina-object toewijzen aan een variabele en een nieuwe methodeketen voor elke pagina instantiëren. Een redelijke prijs voor een consistent en betrouwbaar patroon voor het testen van hergebruik van code.

module.exports = {
  tags: ['accounts', 'passwords', 'users', 'entry'],
  'User can sign up.': function (client) {

    const signupPage = client.page.signupPage();
    const indexPage = client.page.indexPage();

    client.page.signupPage()
      .navigate()
      .checkElementsRendered()
      .signup('Alice', 'Doe', '[email protected]', 'alicedoe')
      .pause(1500, client);

    indexPage.expect.element('#indexPage').to.be.present;
    indexPage.expect.element('#authenticatedUsername').text.to.contain('Alice Doe');
  },
}


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow