Unit test с moment.js

Я новичок в написании модульных тестов и нуждаюсь в некоторой части справки по тестированию функции.

Моя функция выглядит так:

getData() {
return this.parameters.map(p => {
        return {
            name: p.name,
            items: p.items.map(item => {

                const toTime = item.hasOwnProperty('end') ? moment.utc(item.end._d).unix() : null;
                const fromTime = item.hasOwnProperty('start') ? moment.utc(item.start._d).unix() : null;

                return {
                    id: item.id,
                    fromTime: fromTime,
                    toTime: toTime,
                };
            }),
        };
    });
}

и до сих пор мой тест выглядит так (жасмин)

describe('getData()', function() {
it('should return json data', function() {
    $ctrl.parameters = [{
        name: 'test',
        items: [{
            id: 1,
            fromTime: null,
            toTime: null
        }, {
            id: 13,
            fromTime: null,
            toTime: null

        }]
    }];

    expect($ctrl.getData()).toEqual([{
        name: 'test',
        items: [{
            id: 1,
            fromTime: null,
            toTime: null
        }, {
            id: 13,
            fromTime: null,
            toTime: null
        }]
    }]);
});
});

Этот тест работает/проходит, но, как вы можете видеть, я не тестирую троичный if/else, который использует Moment.js. В основном, что троица проверяет, содержит ли элементы свойство start/end, и если это так, преобразуйте это значение в временную метку эпохи /unix и назначьте его либо toTime, либо fromTime. Поэтому, если элементы имеют свойство, называемое end со значением 'Sat Oct 31 2015 00:00:00 GMT+0000 (GMT)', тогда оно будет преобразовано в '1446249600' и назначено на toTime

Надеюсь, это объяснит! Я не уверен, как написать тест для него и буду признателен за любую помощь/предложения.

Ответ 1

Самый простой вариант - просто создать пару дат примера вручную для ввода. Например:

$ctrl.parameters = [{
    name: 'test',
    items: [{
        id: 1,
        start: moment.utc('2017-01-01T01:00:00'),
        end: moment.utc('2017-01-01T06:00:00')
    }, {
        id: 13,
        start: moment.utc('2017-01-02T08:00:00'),
        end: null

    }]
}];

(Обратите внимание, что в приведенном выше примере я изменил fromTime и toTime на start и end соответственно, так как это то, что ожидает getData для ввода.)

Затем определите их временные метки unix. Вы можете сделать эту часть извне - например, я только что открыл инструменты разработчика браузера (F12) на веб-сайте moment.js, оценил следующие инструкции в консоли и захватил значения временной метки:

moment.utc('2017-01-01T01:00:00').unix()
moment.utc('2017-01-01T06:00:00').unix()
moment.utc('2017-01-02T08:00:00').unix()

Наконец, вернемся к unit test, просто убедитесь, что отметки времени соответствуют ожидаемым значениям:

expect($ctrl.getData()).toEqual([{
  name: 'test',
  items: [{
    id: 1,
    fromTime: 1483232400,
    toTime: 1483250400
  }, {
    id: 13,
    fromTime: 1483344000,
    toTime: null
  }]
}]);

В качестве альтернативы, если вы не хотите иметь жесткие временные метки в своих модульных тестах, вместо этого можете сохранить каждую примерную дату в своей собственной переменной (например, start1, end1), а затем сравнить с, например, start1.unix():

// Arrange
const start1 = moment.utc('2017-01-01T01:00:00');
const end1 = moment.utc('2017-01-01T06:00:00');
const start2 = moment.utc('2017-01-02T08:00:00');
$ctrl.parameters = [{
    name: 'test',
    items: [{
        id: 1,
        start: start1,
        end: end1
    }, {
        id: 13,
        start: start2,
        end: null

    }]
}];

// Act
const result = $ctrl.getData();

// Assert
expect(result).toEqual([{
  name: 'test',
  items: [{
    id: 1,
    fromTime: start1.unix(),
    toTime: end1.unix()
  }, {
    id: 13,
    fromTime: start2.unix(),
    toTime: null
  }]
}]);

Это прекрасно, поскольку unit test предназначен для проверки вашего кода, а не момента .js. Это зависит от вас.

Обратите внимание, что я использую шаблон Arrange-Act-Assert для организации теста. Опять же, до вас, но как только ваши юнит-тесты начнут усложняться, это, как правило, упрощает работу.

В любом случае вам нужно будет изменить способ вычисления toTime и fromTime в методе getData, так как написанный код не будет работать, если вы перейдете в null для start или end. В частности, item.hasOwnProperty('start') вернет true, если вы передадите значение null для start, но оно будет ошибочно, потому что оно пытается оценить item.start._d. Вместо этого я рекомендую изменить эти две строки на следующее:

const toTime = item.end ? moment.utc(item.end._d).unix() : null;
const fromTime = item.start ? moment.utc(item.start._d).unix() : null;

Я также советовал бы использовать свойство _d объекта момента, поскольку это внутренняя (закрытая) переменная. Поскольку start и end уже являются объектами момента, вы можете просто сделать это:

const toTime = item.end ? item.end.unix() : null;
const fromTime = item.start ? item.start.unix() : null;

Полный пример jsbin, содержащий все вышеперечисленные рекомендуемые изменения, можно найти здесь:

https://jsbin.com/xuruwuzive/edit?js,output

Обратите внимание, что необходимо сделать некоторые настройки, поэтому он запускается вне контекста AngularJS (make getData - автономная функция, которая принимает свои параметры напрямую, а не через $ctrl).