Alkalmazások fejlesztése

6. előadás

Horváth Győző
Egyetemi adjunktus
1117 Budapest, Pázmány Péter sétány 1/c., 2.420-as szoba
Tel: (1) 372-2500/1816
horvath.gyozo@inf.elte.hu

Eddig

  • Alkalmazás architektúrák (MVC)
  • HTTP, Express
  • Statikus és dinamikus tartalom kiszolgálása
  • Űrlapfeldolgozás
  • Adattárolás, ORM
  • Kód szervezése
  • Hitelesítés és jogosultságkezelés

Ezen a héten

  • Tesztelés
  • Kitekintés
  • Információk a beadandóról

Tesztelés

Tesztek típusai

  • Egységteszt
  • Integrációs teszt
  • End-to-end teszt
  • Funkcionális teszt
  • Regressziós teszt

Egységtesztelés

Mit teszteljünk?

  • Egység
    • models
    • controllers
    • viewmodels

Hogyan teszteljünk?

Mocha

Telepítés

npm install mocha --save-dev

Fájlnevek: <valami>.test.js vagy <valami>.spec.js

A tesztek futtatása

mocha **/*.test.js

Mocha

A tesztkeretrendszer parancsai

describe('Valami', function () {

    before(     function () {/* ... */});
    after(      function () {/* ... */});
    beforeEach( function () {/* ... */});
    afterEach(  function () {/* ... */});

    it('should do something', function () {/* ... */});
    it('should do something', function (done) {/* ... */ done(); }); //Aszinkron tesztek

    // Egymásba ágyazhatóak
    describe('Valami2', function () {
        it('should work', function () {/* ... */});
    });

});

Chai

Telepítés

npm install chai --save-dev

Használat

var expect = require('chai').expect;

expect(12).to.be.equal(12);
expect(true).to.be.true;

Feladat

Végezd el a modellek egységtesztelését!

1. lépés

A teszt keretrendszer telepítése és konfigurálása

package.json

"scripts": {
    "test": "node_modules/mocha/bin/mocha **/*.test.js",
    "watch-test": "node_modules/mocha/bin/mocha --watch **/*.test.js",
    "start": "node server.js"
}

Használat

npm test
npm run watch-test

2. lépés

Tesztpróba

var expect = require("chai").expect;

var Waterline = require('waterline');
var waterlineConfig = require('../config/waterline');
var userCollection = require('./user');
var errorCollection = require('./error');

var User;

before(function (done) {
    // ORM indítása
    var orm = new Waterline();

    orm.loadCollection(Waterline.Collection.extend(userCollection));
    orm.loadCollection(Waterline.Collection.extend(errorCollection));
    waterlineConfig.connections.default.adapter = 'memory';

    orm.initialize(waterlineConfig, function(err, models) {
        if(err) throw err;
        User = models.collections.user;
        done();
    });
});

describe('UserModel', function () {

    beforeEach(function (done) {
        User.destroy({}, function (err) {
            done();
        });
    });
    
    it('should work', function () {
        expect(true).to.be.true;
    });

});

3. lépés

User létrehozása

it('should be able to create a user', function () {
    return User.create({
            neptun: 'abcdef',
            password: 'jelszo',
            surname: 'Gipsz',
            forename: 'Jakab',
            avatar: '',
    })
    .then(function (user) {
        expect(user.neptun).to.equal('abcdef');
        expect(bcrypt.compareSync('jelszo', user.password)).to.be.true;
        expect(user.surname).to.equal('Gipsz');
        expect(user.forename).to.equal('Jakab');
        expect(user.avatar).to.equal('');
    });
});

4. lépés

Keresés

function getUserData() {
    return {
        neptun: 'abcdef',
        password: 'jelszo',
        surname: 'Gipsz',
        forename: 'Jakab',
        avatar: '',
    };
}

it('should be able to find a user', function() {
    return User.create(getUserData())
    .then(function(user) {
        return User.findOneByNeptun(user.neptun);
    })
    .then(function (user) {
        expect(user.neptun).to.equal('abcdef');
        expect(bcrypt.compareSync('jelszo', user.password)).to.be.true;
        expect(user.surname).to.equal('Gipsz');
        expect(user.forename).to.equal('Jakab');
        expect(user.avatar).to.equal('');
    });
});

5. lépés

Módosítás

it('should be able to update a user', function() {
    var newAvatar = 'http://example.com/apple.gif';
    
    return User.create(getUserData())
    .then(function(user) {
        var id = user.id;
        return User.update(id, { avatar: newAvatar });
    })
    .then(function (userArray) {
        var user = userArray.shift();
        expect(user.neptun).to.equal('abcdef');
        expect(bcrypt.compareSync('jelszo', user.password)).to.be.true;
        expect(user.surname).to.equal('Gipsz');
        expect(user.forename).to.equal('Jakab');
        expect(user.avatar).to.equal(newAvatar);
    });
});

6. lépés

Validációk

it('should throw error for invalid data: avatar', function () {
    var userData = getUserData();

    userData.avatar = 'rossz';
    
    expect(createUser(userData)).to.throw;
});

7. lépés

Validációk hatékonyabban

[
    {name: 'surname', value: ''},
    {name: 'forename', value: ''},
    {name: 'neptun', value: ''},
    {name: 'password', value: ''},
    {name: 'role', value: ''},
    {name: 'avatar', value: 'wrong'},
].forEach(function (attr) {
    it('should throw error for invalid data: ' + attr.name, function () {
        var userData = getUserData();

        userData[attr.name] = attr.value;
        
        expect(createUser(userData)).to.throw;
    });    
});

8. lépés

validPassword metódus

describe('#validPassword', function() {
    it('should return true with right password', function() {
         return User.create(getUserData()).then(function(user) {
             expect(user.validPassword('jelszo')).to.be.true;
         })
    });
    it('should return false with wrong password', function() {
         return User.create(getUserData()).then(function(user) {
             expect(user.validPassword('titkos')).to.be.false;
         })
    });
});

Funkcionális tesztelés

Mit teszteljünk?

  • Funkciók
    • oldalak megjelennek-e a végpontokon?
    • megfelelő tartalom jelenik-e meg?
    • jól működik-e az oldal?
    • megfelelő hibaüzenet jelenik-e meg?
    • a folyamatok végigvihetők-e?

(Amit kézzel is tesztelnénk...)

Hogyan teszteljünk?

Zombie.js

Telepítés

npm install zombie --save-dev

# Cloud 9
nvm use iojs-v1.7

Használat

// Kezdeti lépések
var Browser = require('zombie');
Browser.localhost(process.env.IP, process.env.PORT);
var browser = new Browser();

// Böngésző utasítása
browser.visit(url);
browser.fill('name', 'value');
browser.pressButton('css selector');
browser.clickLink('css selector');

// Ellenőrző függvények
browser.assert.success();
browser.assert.redirected();
browser.assert.url({ pathname: 'path' });
browser.assert.text('css selector', 'expected text');
browser.assert.element('css selector');
browser.assert.hasClass('css selector', 'classname');

Selenium IDE

Telepítés

  • test suite
  • test case
  • rögzítés és finomhangolás
  • lejátszás

Feladat

Készítsd el a hibajegy felvételéhez tartozó funkciók tesztjét zombie.js-sel!

1. lépés

Keret

var Browser = require('zombie');

Browser.localhost(process.env.IP, process.env.PORT);

describe('User visits index page', function() {
    var browser = new Browser();
    
    before(function() {
        return browser.visit('/');
    });
});

2. lépés

Főoldal tesztelése

it('should be successful', function() {
    browser.assert.success();
});

it('should see welcome page', function() {
    browser.assert.text('div.page-header > h1', 'Hibabejelentés az IK-n');
});

3. lépés

Hitelesítés tesztelése

describe('User visits new error page', function (argument) {

    var browser = new Browser();
    
    before(function() {
        return browser.visit('/errors/new');
    });
    
    it('should go to the authentication page', function () {
        browser.assert.redirected();
        browser.assert.success();
        browser.assert.url({ pathname: '/login' });
    });
    
    it('should be able to login with correct credentials', function (done) {
        browser
            .fill('neptun', 'k81dnc')
            .fill('password', 'jelszo')
            .pressButton('button[type=submit]')
            .then(function () {
                browser.assert.redirected();
                browser.assert.success();
                browser.assert.url({ pathname: '/errors/list' });
                done();
            });
    });
});

4. lépés

Új hiba felvétele

it('should go the error page', function () {
    return browser.visit('/errors/new')
    .then(function () {
        browser.assert.success();
        browser.assert.text('div.page-header > h1', 'Új hiba bejelentése');
    });
});

it('should show errors if the form fields are not right', function () {
    return browser
        .fill('helyszin', '')
        .fill('leiras', '')
        .pressButton('button[type=submit]')
        .then(function() {
            // browser.assert.redirected();
            browser.assert.success();
            browser.assert.element('form .form-group:nth-child(1) [name=helyszin]');
            browser.assert.hasClass('form .form-group:nth-child(1)', 'has-error');
            browser.assert.element('form .form-group:nth-child(2) [name=leiras]');
            browser.assert.hasClass('form .form-group:nth-child(2)', 'has-error');
        });
});

5. lépés

Hiba felvétele

it('should show submit the right-filled form fields and go back to list page', function() {
    browser
        .fill('helyszin', 'pc6')
        .fill('leiras', 'rossz')
        .pressButton('button[type=submit]')
        .then(function() {
            browser.assert.redirected();
            browser.assert.success();
            browser.assert.url({ pathname: '/errors/list' });
            
            browser.assert.element('table.table');
            browser.assert.text('table.table tbody tr:last-child td:nth-child(2) span.label', 'Új');    
            browser.assert.text('table.table tbody tr:last-child td:nth-child(3)', 'pc6');    
            browser.assert.text('table.table tbody tr:last-child td:nth-child(4)', '0 rossz');
        });
});

Feladat

Készítsd el a hibajegy felvételéhez tartozó funkciók tesztjét Selenium IDE-vel!

Lépések

  • Plugin telepítése
  • Tevékenységek felvétele
  • Use case-ek
    • főoldal
    • hibalista
  • assert-ek használata
  • kézi finomhangolás

Kitekintés

Sails.js

  • MVC-s keretrendszer
  • Magasszintű
  • Projekt főoldala
  • Waterline ORM
  • Websocket integráció

Kraken.js

Információk a beadandóról

Információk