Подтвердить что ты не робот

Полное интеграционное тестирование для NodeJS и клиентской стороны с Yeoman и Mocha

У меня потрясающие клиентские тесты, которые я запускаю с Йоменом. Йомен компилирует мой CoffeeScript, открывает тестовую страницу на сервере, посещает ее с помощью PhantomJS и передает все результаты тестов в командную строку. Процесс довольно хаки, результаты тестирования передаются через alert() сообщения в процесс Phantom, который создает временный файл и заполняет его сообщениями как JSON. Йомен (ну, Grunt) перебирает временный файл, анализирует тесты и отображает их в командной строке.

Причина, по которой я объясняла этот процесс, заключается в том, что я хочу добавить к нему несколько вещей. Я также получил тесты на стороне сервера. Они используют mocha и supertest для проверки конечных точек API и клиента Redis, чтобы убедиться, что состояние базы данных соответствует ожиданиям. Но я хочу объединить эти два набора тестов!

Я не хочу писать ответ на макетирование на стороне клиента для вызовов сервера. Я не хочу посылать серверные данные. Где-то по пути я изменю сервер или клиент, и тест не подведет. Я хочу сделать реальное интеграционное тестирование. Таким образом, всякий раз, когда тест заканчивается на стороне клиента, я хочу, чтобы крючок запускал соответствующий тест на стороне сервера (проверка состояния db, состояние сеанса, переход на другую тестовую страницу).

Есть ли какие-либо решения? Или, сдержанно, где я начинаю взламывать Yeoman/Grunt/grunt-mocha, чтобы сделать эту работу?

Я думаю, что обработчики Phantom в grunt-mocha - это хорошее место для запуска:

// Handle methods passed from PhantomJS, including Mocha hooks.
  var phantomHandlers = {
    // Mocha hooks.
    suiteStart: function(name) {
      unfinished[name] = true;
      currentModule = name;
    },
    suiteDone: function(name, failed, passed, total) {
      delete unfinished[name];
    },
    testStart: function(name) {
      currentTest = (currentModule ? currentModule + ' - ' : '') + name;
      verbose.write(currentTest + '...');
    },
    testFail: function(name, result) {
        result.testName = currentTest;
        failedAssertions.push(result);
    },
    testDone: function(title, state) {
      // Log errors if necessary, otherwise success.
      if (state == 'failed') {
        // list assertions
        if (option('verbose')) {
          log.error();
          logFailedAssertions();
        } else {
          log.write('F'.red);
        }
      } else {
        verbose.ok().or.write('.');
      }
    },
    done: function(failed, passed, total, duration) {
      var nDuration = parseFloat(duration) || 0;
      status.failed += failed;
      status.passed += passed;
      status.total += total;
      status.duration += Math.round(nDuration*100)/100;
      // Print assertion errors here, if verbose mode is disabled.
      if (!option('verbose')) {
        if (failed > 0) {
          log.writeln();
          logFailedAssertions();
        } else {
          log.ok();
        }
      }
    },
    // Error handlers.
    done_fail: function(url) {
      verbose.write('Running PhantomJS...').or.write('...');
      log.error();
      grunt.warn('PhantomJS unable to load "' + url + '" URI.', 90);
    },
    done_timeout: function() {
      log.writeln();
      grunt.warn('PhantomJS timed out, possibly due to a missing Mocha run() call.', 90);
    },

    // console.log pass-through.
    // console: console.log.bind(console),
    // Debugging messages.
    debug: log.debug.bind(log, 'phantomjs')
  };

Спасибо! Там будет щедрость.

4b9b3361

Ответ 1

Я не знаю о Yeoman. Я еще не пробовал - но я все оставил головоломку. Полагаю, вы поймете остальное.

Зачем делать интеграционные тесты?

В вашем вопросе вы говорили о ситуации, когда у вас есть как клиентские тесты, так и серверные тесты, выполняемые с помощью mocks. Я предполагаю, что по какой-то причине вы не можете заставить оба набора тестов работать с одними и теми же макетами. В противном случае, если вы изменили макеты на стороне клиента, ваши тесты на стороне сервера потерпят неудачу, потому что они получат сломанные данные.

Что вам нужно, так это интеграционные тесты, поэтому, когда вы запускаете какой-то код на стороне клиента в своем браузере без браузера, ваш серверный код также будет работать. Более того, просто запустить ваш серверный и клиентский код недостаточно, вы также хотите иметь возможность устанавливать утверждения с обеих сторон, не так ли?

Интеграционные тесты с Node и PhantomJS

В большинстве примеров интеграционных тестов, которые я нашел в Интернете, используйте Selenium или Zombie.js. Первая - это большая платформа на основе Java для управления настоящими браузерами, а позднее - простая оболочка jsdom. Я предполагаю, что вы не решаетесь использовать любой из них и предпочтете PhantomJS. Трудная часть, конечно же, заключается в том, чтобы запустить это из вашего приложения Node. И я получил именно это.

Существует два модуля Node для подключения PhantomJS:

К сожалению, оба проекта кажутся заброшенными их авторами и другими членами сообщества, чтобы развить их и адаптироваться к их потребностям. Это означает, что оба проекта разворачивались много раз, и все вилки почти не работают. API почти не существует. Я провел тесты с одной из вилок phantom (Спасибо, Seb Vincent). Здесь простое приложение:

'use strict';
var express = require('express');

var app = express();

app.APP = {}; // we'll use it to check the state of the server in our tests

app.configure(function () {
    app.use(express.static(__dirname + '/public'));
});

app.get('/user/:name', function (req, res) {
    var data = app.APP.data = {
        name: req.params.name,
        secret: req.query.secret
    };
    res.send(data);
});

module.exports = app;

    app.listen(3000);
})();

Он прослушивает запрос /user и возвращает параметр пути name и параметр запроса secret. Здесь страница, на которой я вызываю сервер:

window.APP = {};

(function () {
    'use strict';

    var name = 'Alex', secret ='Secret';
    var xhr = new XMLHttpRequest();
    xhr.open('get', '/user/' + name + '?secret=' + secret);
    xhr.onload = function (e) {
        APP.result = JSON.parse(xhr.responseText);
    };
    xhr.send();
})();

И вот простой тест:

describe('Simple user lookup', function () {
    'use strict';

    var browser, server;

    before(function (done) {
        // get our browser and server up and running
        phantom.create(function (ph) {
            ph.createPage(function (tab) {
                browser = tab;
                server = require('../app');
                server.listen(3000, function () {
                    done();
                });
            });
        });
    });

    it('should return data back', function (done) {
        browser.open('http://localhost:3000/app.html', function (status) {

            setTimeout(function () {
                browser.evaluate(function inBrowser() {
                    // this will be executed on a client-side
                    return window.APP.result;
                }, function fromBrowser(result) {
                    // server-side asserts
                    expect(server.APP.data.name).to.equal('Alex');
                    expect(server.APP.data.secret).to.equal('Secret');
                    // client-side asserts
                    expect(result.name).to.equal('Alex');
                    expect(result.secret).to.equal('Secret');
                    done();
                });
            }, 1000); // give time for xhr to run

        });
    });
});

Как вы можете видеть, я должен опросить сервер в таймаут. Это потому, что все привязки phantom являются неполными и слишком ограниченными. Как вы можете видеть, я могу проверить состояние клиента и состояние сервера в одном тесте.

Запустите ваши тесты с помощью Mocha: mocha -t 2s Вам, вероятно, потребуется увеличить значение тайм-аута по умолчанию для более развитых тестов для запуска.

Итак, как вы можете видеть, все это выполнимо. Здесь репо с полным примером. p >