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.
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(...
The moment(). unix() function is used to get the number of seconds since the Unix Epoch.
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.
To change the default time zone, use moment. tz. setDefault with a valid time zone.
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
).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With