Sök…


Anmärkningar

Nightwatch har tillhandahållit Acceptance och End-to-End-test för Meteor-appar sedan v0.5 dagar och har hanterat migrationer från PHP till Spark to Blaze och React; och alla större kontinuerliga integrationsplattformar. För ytterligare hjälp, se:

Nightwatch API-dokumentation
Nightwatch.js Google Group

App-yta

På den mest grundläggande nivån är acceptanstest i huvudsak svartboxtestning, som i grund och botten handlar om att testa ingångar och utgångar från ett slutet system. Som sådan finns det tre väsentliga funktioner för acceptansprovning: lokalisera en resurs, läsa data och skriva data. När det gäller webbläsare och webbappar, kokar dessa tre funktioner i princip till följande:

  1. Ladda en webbsida eller applikationsvy
  2. Inspektera användargränssnittselement (dvs. DOM)
  3. Utlösa en händelse / simulera en användarinteraktion

Vi kallar detta applikationens yta. Ytarea är allt som en användare ser eller upplever. Det är utsidan av ett blackbox-system. Och eftersom användare interagerar med moderna webbapplikationer på videoskärmar med webbläsare definieras vår yttäckning av universella resurslokaler (URL) och visningsportar. Och så vårt första genomgång börjar se ut som följande:

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();
  }
};

Anpassade kommandon

Nightwatch stöder att skapa anpassade kommandon som kan simulera tangenttryckningar, musklick och andra ingångar. Ett anpassat kommando kan kedjas med andra Nightwatch-kommandon, så:

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

För att aktivera detta, definiera ett kommando i ./tests/nightwatch/commands/login så:

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.
};

För att allt ska fungera måste du lägga till id attribut till din inloggningssida. På någon nivå måste det grovt se ut som följande:

<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>

Inspekterar Meteorobjekt på klienten

Eftersom Nightwatch har åtkomst till webbläsarkonsolen är det möjligt att inspektera klientsidobjekt med .execute() . I följande exempel kontrollerar vi Sessionsobjektet för en viss sessionvariabel. Först börjar vi med att skapa filen ./tests/nightwatch/api/meteor/checkSession , där vi kommer att behålla följande kommando:

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

Vi kan sedan kedja det så:

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

Formulär och ingångstyper

För att ladda upp en fil måste du först skapa en / datakatalog och lägga till filen du vill ladda upp.

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

Ditt formulär behöver en inmatning med filtyp. (Vissa människor gillar inte stylingsalternativen som denna input tillhandahåller; och ett vanligt mönster är att göra denna input dold; och att ha en annan knapp på sidan klicka på den på användarens vägnar.)

<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>

Dina tester måste sedan använda setValue () och lösa sökvägen till den lokala filtillgången.

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();
  }
};

Tack till Daniel Rinehart för att inspirera detta exempel.

Komponenter och sidobjekt

Sidobjekt liknar anpassade kommandon; förutom att de är samlingar av anpassade kommandon som är associerade med en specifik UI-komponent. Detta fungerar extremt bra med modern komponentbaserad design, till exempel i 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]'
    }
  }
};

Det enda förbehållet med att använda PageObject-mönstret i testkomponenter är att implementeringen bryter metodkedjeflödet som det nativa Nightwatch verify.elementPresent tillhandahåller. Istället måste du tilldela sidobjektet till en variabel och initiera en ny metodkedja för varje sida. Ett rimligt pris att betala för ett konsekvent och tillförlitligt mönster för återanvändning av testkoder.

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
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow