Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit test with moment.js

I'm new to writing unit tests and need some help testing part of a function.

My function looks like this...

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

and so far my test looks like this (jasmine)

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

This test is working/passing, but as you can see I am not testing the ternary if/else that uses Moment.js. Basically what the ternary does is check if items contains a property called start / end and if it does, convert that value to a epoch/unix timestamp and assign it to either toTime or fromTime. So if items had a property called end with a value of 'Sat Oct 31 2015 00:00:00 GMT+0000 (GMT)', then it would be converted to '1446249600' and assigned to toTime

Hopefully that explains it! I'm not sure how to write a test for it and would appreciate any help/suggestions.

like image 751
Jose the hose Avatar asked Dec 14 '16 13:12

Jose the hose


People also ask

How do you mock in MomentJS?

mockImplementation(() => new Date('2019-01-01')); // Test code here Date. now. mockRestore(); // Mocking out Date constructor object (used by moment) const nowString = '2018-02-02T20:20:20'; // Mock out the date object to return a mock null constructor const MockDate = (lastDate) => (... args) => new lastDate(...

What is Moment () Unix ()?

The moment(). unix() function is used to get the number of seconds since the Unix Epoch.

How do you use moments?

You can use MomentJS inside a browser using the script method. It is also available with Node. js and can be installed using npm. In MomentJS, you can find many easy to use methods to add, subtract, validate date, get the maximum, minimum date etc.

How do I set moment time zone?

To change the default time zone, use moment. tz. setDefault with a valid time zone.


1 Answers

The simplest option is to just construct a couple example dates manually for the input. For example:

$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

    }]
}];

(Note in the above example, I changed fromTime and toTime to start and end, respectively, since that is what getData is expecting for the input.)

Then figure out their unix timestamps. You can do this part externally - for example, I just opened up the browser developer tools (F12) on the moment.js website, evaluated the following statements in the console, and grabbed the timestamp values:

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

Finally, back in the unit test, just verify that the timestamps match the expected values:

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

Alternatively, if you would rather not have hardcoded timestamps in your unit tests, you can instead store each example date in its own variable (e.g., start1, end1), and then compare to, e.g., 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
  }]
}]);

That's perfectly fine, since the unit test is meant to test your code, not moment.js. It's up to you.

Note also that I am using the Arrange-Act-Assert pattern for organizing the test. Again, up to you, but once your unit tests start to get complicated, that tends to keep things easier to follow.

Either way, you will need to change how you compute toTime and fromTime in your getData method, since the code as written will not work if you pass in null for either start or end. In particular, item.hasOwnProperty('start') will return true if you pass in a null value for start, but it will error out because it tries to evaluate item.start._d. Instead, I recommend changing those 2 lines to the following:

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

I would also advise against using the _d property of the moment object, since that is an internal (private) variable. Since start and end are already moment objects, you may instead be able to just do this:

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

A complete jsbin example containing all of the above recommended changes is available here:

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

Note that some tweaks had to be made so it runs outside the context of AngularJS (make getData a standalone function that accepts its parameters directly, rather than via $ctrl).

like image 145
Sergey K Avatar answered Oct 07 '22 05:10

Sergey K