Статьи

Функциональное тестирование с Mocha & Chai

При разработке полного стека JavaScript с использованием JavaScript на стороне клиента или на стороне сервера мы хотели бы протестировать поведение ввода-вывода функции. В простейшем виде поведение ввода-вывода функции определяет выходные данные функции в терминах ее ввода.

Например, для функции, добавляющей два числа:

    function f(x,y){
        return x + y;
    }

Его поведение ввода-вывода абстрактно определяет, что

    f(x,y) == x + y

Функции могут выполняться либо локально, либо включать вызовы удаленных служб. Локальное выполнение функции — это классическое внутрипрограммное выполнение, которое не включает никаких вызовов на стороне сервера.

Такие функции могут быть функциями JavaScript, функциями Node.js  и могут быть встроены в такие среды, как AngularJS .

Автоматизированное тестирование

Тестирование абстрактных утверждений, таких как приведенное выше, в большинстве случаев нецелесообразно. Вместо этого автоматическое тестирование пытается автоматически запустить тестовые сценарии, которые проверяют правильность важных случаев. При разумном использовании автоматизированное тестирование может достичь нескольких целей:

  • Он документирует тесты, используемые при ручном тестировании при кодировании
  • Регрессионное тестирование
  • Непрерывная интеграция, основанная на фактах, а не на интуиции

Автоматизированное тестирование требует тестового бегуна, программы, которая понимает тестовые сценарии, написанные на языке тестового бегуна. Тестовый сценарий включает в себя несколько вызовов функций и некоторые утверждения о поведении функций ввода-вывода, а также автоматическую проверку утверждений, выраженных на языке утверждений.

Мокко и чай

Мы нашли Mocha  в качестве участника тестирования, а Chai  в качестве языка утверждений — мощную комбинацию для автоматического тестирования полного стека. В отличие от других участников тестирования, таких как Жасмин , Мокко не привязан к какому-либо конкретному языку утверждений. Сам Chai имеет несколько разновидностей утверждений BDD и TDD. Мы нашли BDD для Chai ожидать стиль очень удобен как для локального выполнения функций, а также для выполнения вызовов с участием к удаленным службам.

Итак, как выглядит тестовый скрипт Mocha?

Тест мокко

Мокко-тест — это встроенные «описательные» операторы, которые могут быть вложенными. Каждое оператор описания является тестовым сценарием. Чтобы на самом деле выполнить тест, используйте оператор «it»:

    describe("function f", function(){
        it("adds numbers", function(done){
            expect(f(3, 4)).to.be.equal(7);
            done();
        });
    });

Утверждение Чая с использованием «ожидаемого» используется для определения поведения функции ввода-вывода.

Обратный вызов «done» используется для перехода к следующему тесту.

Использование мокко с чаем в бэканде

Мы использовали Mocha с Chai в Backand , поставщиком Backend as a Service (BaaS), который автоматически создает реляционную базу данных вместе с REST API из схемы JSON, описывающей базу данных в знакомых терминах JavaScript. Это было вдохновлено Waterline  ORM.

Например, эта схема JSON:

             [{ "name": "R",
                "fields": {
                    C: { type: “integer” },
                    D: { type: “string”, required: true }
                }
              },
             { "name": "U",
               "fields": {
                   E: {     type: “integer” },
                  F: { type: “string”,     required: true },
                  H: { type: “string” }
                  }
                }]

Создает базу данных с двумя отношениями:

    CREATE TABLE `P` (
       `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
       `C` int(11) DEFAULT NULL,
       `D` varchar(255) NOT NULL,
       PRIMARY KEY (`id`)
    ) 
    ENGINE=InnoDB DEFAULT CHARSET=latin1;    
    CREATE TABLE `Q` (
       `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
       `E` int(11) DEFAULT NULL,
       `F` varchar(255) NOT NULL,
       `H` varchar(255) DEFAULT NULL,
       PRIMARY KEY (`id`)
    ) 
    ENGINE=InnoDB DEFAULT CHARSET=latin1;

Используя эти операторы ‘create table’ в MySQL:

    create table `S` (`id` int unsigned not null auto_increment primary key, `C` int, `D` varchar(255) not null);
    create table `U` (`id` int unsigned not null auto_increment primary key, `E` int, `F` varchar(255) not null, `H` varchar(255));

Наши потребности в тестировании

У нас было две основные функции: «проверка», которая проверяет, является ли JSON допустимой реляционной схемой, и «преобразование», которое преобразует одну схему в другую. Backand использует ‘transform’, когда пользователи Backand изменяют структуру своей базы данных, предоставляя новую схему JSON. Функция создает последовательность операторов MySQL для изменения базовой базы данных MySQL.

Во время кодирования мы собрали набор тестовых примеров, и мы нашли Mocha с Chair полезным для их документирования. Это было сделано сразу во время кодирования. Завершив первый этап кодирования, мы расширили использование автоматизированного тестирования до регрессионного тестирования. Мы постоянно расширяем выразительную силу схемы, например, в связи «один ко многим» между отношениями в базе данных. Автоматизированное тестирование позволило проводить удобное регрессионное тестирование.

Письменный тест

Чтобы установить Mocha и Chai, мы использовали «package.json»:

    {
       "name": "test",
       "version": "0.0.0",
       "description": "test orm",
       "author": "Yoram Kornatzky",
       "dependencies": {
             "mocha": "latest",
           "chai": "latest"
       }
    }

Установка их с помощью:

    npm install

В нашем тестовом файле ‘unit_functional_test.js’ мы включаем Chai, а затем наш код:

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

    var validator = require("../validate_schema.js").validator;
    var transformer = require("../transform.js").transformer;

Теперь мы пишем тест, чтобы проверить, что наш валидатор проверяет правильность действительной схемы.

    describe("validator has no false negatives", function(){
          it("declare valid schema with default values and non null columns", function(done){
                var v = validator([{   "name": "R",
                        "fields": {
                              "A": {  "type": "float",  "defaultValue": 20  },
                              "B": {  "type": "string", "required": true }
                     }
                   },
                   {  "name": "U",
                        "fields": {
                           "F": { "type": "string", "required": true },
                           "G": { "type": "float" },
                          "H": { "type": "string" }
                     }
                }]);
                expect(v).to.deep.equal({ valid: true, warnings: [] });
                 done();
           });
    });

Мы запускаем тест с:

    node_modules/mocha/bin/mocha unit_functional_test.js

И получите этот вывод:

Еще один тест для проверки правильности значений полей по умолчанию:

    it("default values of columns are of the right type", function(done){
        var v = validator(
            [     

                 { name: "user",
                  fields: {
                        name: { type: 'string', defaultValue: 200 },
                        age: { type: 'datetime', defaultValue: '2015-09-08' },
                        dogs:{ collection: 'pet', via: 'owner' }
                  }
                },    
                { name: "pet",
                  fields: {
                        name: {     type: 'string' },
                        registered: { type: 'boolean' },
                        owner:{ object: 'user' }
                 }    
                }
            ]
        );
        expect(v).to.deep.equal({ valid: false, 
              warnings: ["column default value should be a string:user name"]});
        done();
    });

Который в этом случае должен обнаружить, что столбец «имя» в таблице «пользователь» имеет недопустимое значение по умолчанию.

Чтобы проверить правильность операторов SQL для преобразования одной схемы базы данных в другую, мы используем этот тест:

      it("drop columns", function(done){
           var r = transformer(
             [
                {  name: "R", 
                   fields: {
                        a: { type: "float" },
                        b: { type: "string" },
                        dogs: { collection: "U", via: "owner" }
                   }
                },
                { name: "U", 
                  fields: {
                        c: { type: "float" },
                        d: { type: "string" },
                        owner: { object: 'R' }
                   }
                }
            ],
            [
                { name: "R", 
                   fields: {
                        b: { type: "string" }
                   }
                },
                { name: "U", 
                  fields: {
                        c: { type: "float" },
                        d: { type: "string" }
                   }
                }
            ]
        );
        expect(r).to.deep.equal(
            { 
                "alter": [
                      "alter table `R` drop `a`",
                      "alter table `U` drop `owner`"           
                ]
                });

Тестирование вызовов удаленной службы

При разработке полного стека практически любой модуль, будь то на стороне клиента или на стороне сервера, включает вызовы удаленных сервисов. Такие вызовы, будучи асинхронными, на самом деле являются вызовами функций, которые дают выход для ввода. Мокко и Чай также удобны для тестирования таких звонков.

В Backand мы получаем подробную информацию о базе данных MySQL, когда вы указываете свое имя пользователя, пароль и имя приложения. Это описано в следующем тесте, где мы используем пакет ‘request’ для выполнения HTTP-вызовов.

Во-первых, нам требуется, чтобы наш модуль выполнял вызовы удаленного сервиса:

    var connectionInfo = require("../get_connection_info");

Затем мы звоним, чтобы получить токен авторизации для авторизованного пользователя и приложения. Как только мы получим токен, мы вызываем сервис getConnectionInfo. Этот тест предназначен для проверки поведения ввода-вывода службы getConnectionInfo. Таким образом, утверждения находятся на выходе этого сервиса.

    describe("get connection info", function(){
          it("get connection info with correct credentials", function(done){
               this.timeout(4000);

           // credentials
               var email = "johndoe@example.com";
               var password = "secret";
               var appName = "myApp";

           // request authentication token
               request(
                     {
                        url: tokenUrl,   
                       method: 'POST',
                       form: {
                          username: email,
                          password: password,
                          appName: appName
                       }
                     }, 
                     function(error, response, body){
                          if (!error && response.statusCode == 200) {
                                 var b = JSON.parse(body)
                                 var accessToken = b["access_token"];
                                 var tokenType = b["token_type"];

                        // now call the remote service
                                 connectionInfo.getConnectionInfo(accessToken, 
tokenType, appName, 
                            function(err, result){
                                           expect(result).to.deep.equal(
                                              { 
                                                    hostname: 'cd2junihlkms.aws.com',
                                                   port: '3306',
                                                   db: 'backandmyapp',
                                                   username: 'lmezyl4j',
                                                   password: ‘our_db_secret’ 
                                               }
                                           );
                                           done();
                                  });
                           }
                           else{
                                  done();
                              }
                    }
               );
          });
     });

Мы увеличиваем стандартное время ожидания Mocha с 2000 мс до 4000 мс, поскольку вызов серверу может занять некоторое время.