How do I mock Date.toLocaleDateString in jest? - javascript

I have this codepiece in my React component, which renders an HTML in the end:
new Date(createDate).toLocaleDateString()
My local machine and our build machine have different locales set, so the result of this function is not coherent. So as you'd expect, the unit test passes on my machine and fails on build machine, or vice versa.
I want to mock "toLocalDateString" so that it always uses the same locale, say 'en-US', or at least it always returns the same string. Our test framework is jest. How do I achieve this goal?
I tried this in my test.spec.js but it didn't have any effect at all:
Date.prototype.toLocaleDateString = jest.fn().mockReturnValue('2020-04-15')
expect(component).toMatchSnapshot()
I still get the same old toLocalDateString implementation in the snapshot, my mockReturnValue is not taken into account.

I may be a bit late but hope it helps someone
let mockDate;
beforeAll(() => {
mockDate = jest.spyOn(Date.prototype, 'toLocaleTimeString').mockReturnValue('2020-04-15');
});
afterAll(() => {
mockDate.mockRestore();
});

Can you wrap
new Date(createDate).toLocaleDateString()
in a function, pass it as prop to component and then mock it?

Would the below code work well for you? I am mocking the date object this way.
const realDateToLocaleDateString = Date.prototype.toLocaleDateString.bind(global.Date);
const toLocaleDateStringStub = jest.fn(() => '2020-04-15');
global.Date.prototype.toLocaleDateString = toLocaleDateStringStub;
const date = new Date();
console.log(date.toLocaleDateString()); // returns 2020-04-15
global.Date.prototype.toLocaleDateString = realDateToLocaleDateString;

Related

how to test class methods with jest (on return values)

Im trying to write unit tests for class methods with jest ( new to jest)
I have methods that e.g. take arrays and modify them and bring them into different form to satisfy algorithm needs.
But I dont see a way how I simply can test class method receiving and returning values.
Looks like there is a problem with classes, class methods cant be tested as simple functions.
But if I look at the docs I dont see it covering these topics, it only covers e.g. was a class instance called, was a class method called..
Edited:
this is my code example
import MyClass from "../MyClass.js";
// mocked data
const inputArrayMock=[{someObject}]
const outputArrayMock=[{modifiedObject}]
test("test MyClass method a", () => {
const obj = new MyClass();
const result = obj.methodA(inputArrayMock);
expect(result).toEqual(outputArrayMock);
});
I just ran my code again, it's throwing the error:
Received: {Symbol(async_id_symbol): 293, Symbol(trigger_async_id_symbol): 281, Symbol(destroyed): {"destroyed": false}}
Note: Both arrays (in- and output values I wrote as mock data. The expected array is correct, but the received not, which throws the error.
Without knowing your code I can only make a guess.
import MyClass from "../MyClass.js";
test("your test name", () => {
const obj = new MyClass();
const result = obj.theMethodYouWantToCheck();
expect(result).toBe("the desired result");
});
I had a similar issue and it turned out to be async/await related. Again without knowing the class or method your calling, this is just an educated guess.
Try adding async to the test method and call the method with await.
import MyClass from "../MyClass.js";
test("your test name", async () => {
const obj = new MyClass();
const result = await obj.theMethodYouWantToCheck();
expect(result).toBe("the desired result");
});
This fixed the issue for me. Don't know if its still an issue you're facing, as it's an 8 month old ticket but just for any future googlers.

How do you mock just certain parts of a module with jest?

I've built a calendar around moment.js and I'm working on unit tests right now.
The first problem I solved was how the date will change when the tests run, so I've been able to lock down the moment using this guidance.
Currently, I'm stuck on an error:
"TypeError: Cannot read property 'weekdaysShort' of undefined"
My code has a line: const dateHeaders = moment.weekdaysShort();
By implementing the mocked moment().format(), I've essentially lost the rest of the library.
My immediate question is how I can set up jest to let me return the array that you get from moment.weekdaysShort();
My larger question is whether I've gone down the wrong path and should come up with another strategy.
Things I've tried with unsuccessful results
Manually adding in the weekdayShort function:
const mockMoment = function() {
return {format: '2016–12–09T12:34:56+00:00'}
};
mockMoment['weekdaysShort'] = () => ['Sun', 'Mon', 'Tues']; // etc etc etc
jest.mock('moment', () => mockMoment);
Assembling a manual mock in a __mocks__ folder. I didn't go too far down this path because it started to feel like I'd have to copy/paste the entire Moment.js library into the mock. And while it'd be cool to figure out how they do what they do, that's a project for another day.
jest.spyOn - doesn't work because I'm not spying on a module.
At this point, I'm considering abandoning the Moment function for an array passed in through props. And while I'm confident that'll get me past this problem, it feels like I'm gonna hit another roadblock quickly afterwards.
Thanks in advance for the help.
Just found out the pattern I commonly use was provided in a 2016 github thread and, in all honesty, that's probably where I found it even though I don't specifically remember it :)
jest.mock('moment', () =>
const original = jest.requireActual('moment');
return {
__esModule: true,
default: {
...original,
...just the parts you want to mock
}
}
);
MomentJS and Jest don't play well together. The requireActual method will be awesome, but even providing the following yielded a component that wouldn't render.
jest.mock('moment', () =>
const original = jest.requireActual('moment');
return {
__esModule: true,
default: {
...original,
}
}
);
Ultimately, I was able to lock the date down to a single moment by mocking the Javascript Date object, on which MomentJS builds all of this functionality.
describe('Calendar', () => {
let dateNowSpy;
beforeAll(() => {
dateNowSpy = jest.spyOn(Date, 'now').mockImplementation(() => 1487076708000);
});
afterAll(() => {
dateNowSpy.mockRestore();
});
it('does things, () => {
// all the things!
}
}

Mocking a Node Module which uses chained function calls with Jest in Node

Allow me to note that a similar question to this one can be found here, but the accepted answer's solution did not work for me. There was another question along the same lines, the answer of which suggested to directly manipulate the function's prototypes, but that was equally non-fruitful.
I am attempting to use Jest to mock this NPM Module, called "sharp". It takes an image buffer and performs image processing/manipulation operations upon it.
The actual implementation of the module in my codebase is as follows:
const sharp = require('sharp');
module.exports = class ImageProcessingAdapter {
async processImageWithDefaultConfiguration(buffer, size, options) {
return await sharp(buffer)
.resize(size)
.jpeg(options)
.toBuffer();
}
}
You can see that the module uses a chained function API, meaning the mock has to have each function return this.
The Unit Test itself can be found here:
jest.mock('sharp');
const sharp = require('sharp');
const ImageProcessingAdapter = require('./../../adapters/sharp/ImageProcessingAdapter');
test('Should call module functions with correct arguments', async () => {
// Mock values
const buffer = Buffer.from('a buffer');
const size = { width: 10, height: 10 };
const options = 'options';
// SUT
await new ImageProcessingAdapter().processImageWithDefaultConfiguration(buffer, size, options);
// Assertions
expect(sharp).toHaveBeenCalledWith(buffer);
expect(sharp().resize).toHaveBeenCalledWith(size);
expect(sharp().jpeg).toHaveBeenCalledWith(options);
});
Below are my attempts at mocking:
Attempt One
// __mocks__/sharp.js
module.exports = jest.genMockFromModule('sharp');
Result
Error: Maximum Call Stack Size Exceeded
Attempt Two
// __mocks__/sharp.js
module.exports = jest.fn().mockImplementation(() => ({
resize: jest.fn().mockReturnThis(),
jpeg: jest.fn().mockReturnThis(),
toBuffer:jest.fn().mockReturnThis()
}));
Result
Expected mock function to have been called with:
[{"height": 10, "width": 10}]
But it was not called.
Question
I would appreciate any aid in figuring out how to properly mock this third-party module such that I can make assertions about the way in which the mock is called.
I have tried using sinon and proxyquire, and they don't seem to get the job done either.
Reproduction
An isolated reproduction of this issue can be found here.
Thanks.
Your second attempt is really close.
The only issue with it is that every time sharp gets called a new mocked object is returned with new resize, jpeg, and toBuffer mock functions...
...which means that when you test resize like this:
expect(sharp().resize).toHaveBeenCalledWith(size);
...you are actually testing a brand new resize mock function which hasn't been called.
To fix it, just make sure sharp always returns the same mocked object:
__mocks__/sharp.js
const result = {
resize: jest.fn().mockReturnThis(),
jpeg: jest.fn().mockReturnThis(),
toBuffer: jest.fn().mockReturnThis()
}
module.exports = jest.fn(() => result);

Components using Date objects produce different snapshots in different timezones

I'm using Enzyme with enzyme-to-json to do Jest snapshot testing of my React components. I'm testing shallow snapshots of a DateRange component that renders a display field with the current range (e.g. 5/20/2016 - 7/18/2016) and two DateInput components that allow selecting a Date value. This means that my snapshot contains the Dates I pass to the component both in the DateInput props and in a text representation it resolves itself. In my test I'm creating some fixed dates using new Date(1995, 4, 23).
When I run my test in different timezones, this produces different snapshots, because the Date(year, month, ...) constructor creates the date in the local timezone. E.g. use of new Date() produces this difference in snapshot between runs in my local timezone and on our CI server.
- value={1995-05-22T22:00:00.000Z}
+ value={1995-05-23T00:00:00.000Z}
I tried removing the timezone offset from the dates, but then the snapshot differed in the display field value, where the local timezone-dependent representation is used.
- value={5/20/2016 - 7/18/2016}
+ value={5/19/2016 - 7/17/2016}
How can I make my tests produce the same Dates in snapshots regardless of the timezone they're run in?
I struggled with this for hours/days and only this worked for me:
1) In your test:
Date.now = jest.fn(() => new Date(Date.UTC(2017, 7, 9, 8)).valueOf())
2) Then change the TZ env var before running your tests.
So the script in my package.json:
(Mac & Linux only)
"test": "TZ=America/New_York react-scripts test --env=jsdom",
(Windows)
"test": "set TZ=America/New_York && react-scripts test --env=jsdom",
I ended up with a solution comprised of two parts.
Never create Date objects in tests in timezone-dependent manner. If you don't want to use timestamps directly to have readable test code, use Date.UTC, e.g.
new Date(Date.UTC(1995, 4, 23))
Mock the date formatter used to turn Dates into display values, so that it returns a timezone-independent representation, e.g. use Date::toISOString(). Fortunately this was easy in my case, as I just needed to mock the formatDate function in my localization module. It might be harder if the component is somehow turning Dates into strings on its own.
Before I arrived at the above solution, I tried to somehow change how the snapshots are created. It was ugly, because enzyme-to-json saves a local copy of toISOString(), so I had to use _.cloneDeepWith and modify all the Dates. It didn't work out for me anyway, because my tests also contained cases of Date creation from timestamps (the component is quite a bit more complicated than I described above) and interactions between those and the dates I was creating in the tests explicitly. So I first had to make sure all my date definitions were referring to the same timezone and the rest followed.
Update (11/3/2017): When I checked enzyme-to-json recently, I haven't been able to find the local saving of toISOString(), so maybe that's no longer an issue and it could be mocked. I haven't been able to find it in history either though, so maybe I just incorrectly noted which library did it. Test at your own peril :)
I did this by using timezone-mock, it internally replaces the global Date object and it's the easiest solution I could find.
The package supports a few test timezones.
import timezoneMock from 'timezone-mock';
describe('when in PT timezone', () => {
beforeAll(() => {
timezoneMock.register('US/Pacific');
});
afterAll(() => {
timezoneMock.unregister();
});
// ...
https://www.npmjs.com/package/timezone-mock
I ended up getting around this by mocking the toLocaleString (or whatever toString method you are using) prototype. Using sinon I did:
var toLocaleString;
beforeAll(() => {
toLocaleString = sinon.stub(Date.prototype, 'toLocaleString', () => 'fake time')
})
afterAll(() => {
toLocaleString.restore()
})
This way if you are generating strings straight from a Date object, you're still OK.
2020 solution that works for me
beforeEach(() => {
jest.useFakeTimers('modern');
jest.setSystemTime(Date.parse(FIXED_SYSTEM_TIME));
});
afterEach(() => {
jest.useRealTimers();
});
If you're using new Date() constructor instead of Date.now you can do like below:
const RealDate = Date;
beforeEach(() => {
// #ts-ignore
global.Date = class extends RealDate {
constructor() {
super();
return new RealDate("2016");
}
};
})
afterEach(() => {
global.Date = RealDate;
});
This issue is a must visit if you're here.
Adding TZ=UTC to my .env file solved the issue for me.
A simple fact can make it easy.
Just use :
new Date('some string').
This will always give an invalid date and no matter which machine, it will always be invalid date.
cheers.
Try passing a random date like new Date(1466424490000), wherever you call new Date()

Stubbing variables in a constructor?

I'm trying to figure out how to properly stub this scenario, but i'm a little stuck.
The scenario is, i've got a db.js file that has a list of couchdb databases in it (each database contains tweet entries for a particular year).
Each year a new database is created and added to this list to hold the new entries for that year (so the list of databases isn't constant, it changes each year).
So my db.js file looks like this:
var nano = require('nano')(`http://${host}`);
var databaseList = {
db1: nano.use('db2012'),
db2: nano.use('db2013'),
db4: nano.use('db2014'),
db5: nano.use('db2015'),
db6: nano.use('db2016')
};
module.exports.connection = nano;
module.exports.databaseList = databaseList;
And event.js (a simple model file), before methods are added looks like this:
var lastInObject = require('../../helpers/last_in_object');
var db = require('../../db');
var EventModel = function EventModel() {
this.connection = db.connection;
this.databaseList = db.databaseList;
this.defaultDatabase = lastInObject(db.databaseList);
};
EventModel.prototype.findAll =
function findAll(db, callback) {/* ... */}
My question is, how do i stub the databaseList, so i can safely test each of the model methods without having any brittleness from the growing databaseList object?
Ideally i'd like to be able hijack the contents of the databaseList in my tests, to mock different scenarios, but i'm unsure how to tackle it.
Here's an example test, to ensure the defaultDatabase property is always pointing to the last known event, but obviously i don't want to have to update this test every year, when databaseList changes, as that makes the tests very brittle.
it('should set the default database to the last known event', () => {
var Event = require('../event');
var newEventModel = new Event();
expect(newEventModel.defaultDatabase.config.db)
.to.equal('db2014');
});
Suggestions welcome! If i've gone about this wrong, let me know what i've done and how i can approach it!
Also, this is just a scenario, i do have tests for lastInObject, i'm more interested in how to mock the concerning data.
In my opinion you need to stub the whole "db" module. That way you won't have any real db connection and you can easily control the environment of your tests. You can achieve this by using the mockery module.
That way you can stub the object that the require('../../db') returns. This will allow you to set whatever value you like in the properties of that object.
Building upon Scotty's comment in the accepted answer a bit... here's some code I've got which seems to do the job. No guarantees that this is a good implementation, but it does successfully stub out the insert and get methods. Hopefully it helps someone. :)
// /src/common/database.js
const database = require('nano')('http://127.0.0.1/database');
module.exports = {
async write(document, documentId) {
return await new Promise(resolve => database.insert(document, documentId, resolve));
},
async read(documentId){
return await new Promise(resolve => database.get(documentId, resolve));
}
};
// /test/setup.js
const proxyquire = require('proxyquire');
proxyquire('../src/common/database.js', {
nano() {
return {
insert(document, documentId, callback){ callback(); },
get(documentId, callback){ callback(); }
};
}
});

Categories

Resources