Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spy on global date has very weird behaviors #9185

Closed
danielo515 opened this issue Nov 14, 2019 · 6 comments
Closed

Spy on global date has very weird behaviors #9185

danielo515 opened this issue Nov 14, 2019 · 6 comments
Labels

Comments

@danielo515
Copy link

danielo515 commented Nov 14, 2019

Hello,
I have some test where I need to control what new Date returns, so I went through the way of spy on global.date. However, it is not honouring the implementation very well, and the behavior I'm getting is quite strange.

If I go with this simple implementation:

const mockDate = jest.spyOn(global, 'Date');
mockDate.mockImplementation(x => x );

What I get as output is just an empty object instead of the symbol I'm passing into it.
For some reason, changing the implementation to something like this, makes it work (in some situations)

const mockDate = jest.spyOn(global, 'Date');
mockDate.mockImplementation(x => ({date: x}) );

Then, it returns {date: symbol('whatever')}, which I can use to check (although is a bit weird).
Things start to get funny when such value is passed to another mock function, something equally simple:

const parse = {
  makeDate: jest.fn(x => x).mockName('makeDate'),
};

The code I'm testing calls this mock function like this:

 parse.makeDate(new Date(startDate))  // startDate is a Symbol on my tests

So later I have to do expect(parse.makeDate).toHaveBeenCalledWith({ date: startDate });.
Is there any other way to have a more simplistic implementation of this?

@mayankmandloi
Copy link

have you gone thru previous issues?
I found one which probably address your problem
Mocking current time for Date

@danielo515
Copy link
Author

Yes, I tried going through previous issues but didn't found one that was close enough to my problem. I'll take a look at that one

@yacinehmito
Copy link
Contributor

Hello 👋

In the first case, new Date() will give you an empty object because x => x is called as a constructor. When something is returned in a constructor, the behaviour in JavaScript is the following:

  1. if the value is a primitive, it is ignored
  2. if the value is an object, it is returned instead of the newly created object

So, when you just use the symbol, you end up in case 1. The symbol is ignored, so a new object is created and returned. It is empty because the constructor your are using is a simple function that does not mutate this (and has nothing in its prototype property).

When you use { date: symbol('whatever') }, the constructor now returns an object, which means that we are in case 2. You get back was is returned; it's still not a Date.

Is there any other way to have a more simplistic implementation of this?

It depends on what your are trying to achieve, why you are using a symbol in the first place and how you want to make your assertion.

As far as I am concerned my experience with mocking Date has been pretty straightforward but the setup to make is pretty involved. I usually use the following code:

// Save the Date constructor for later use
const Date = global.Date;

// When calling `new Date()` in the code that is tested, it delegated to the actual `Date`
// This doesn't work at all when code calls only `Date()`
const mockDate = jest.fn((...args) => {
  return new Date(...args)
});

// This is to make `mockDate` look pretty close to the actual `Date`
mockDate.name = 'Date';
mockDate.prototype = Date.prototype;

// Mocking static functions as well
mockDate.now = jest.fn(Date.now);
mockDate.parse = jest.fn(Date.parse);
mockDate.UTC = jest.fn(Date.UTC);

// `mockDate.mockReturnValue` doesn't seem to work when `Date` is called as a constructor
// This is a little hack that also has the benefit of checking that we return an actual `Date`
mockDate.mockReturnDate = (date) => {
  if (!(date instanceof Date)) {
    throw new TypeError('Value provided to mockDate is not a Date');
  }
  return mockDate.mockImplementation(() => date);
};

// Replaces `Date` by the mock
global.Date = mockDate;

I have no idea if this is the recommended way of mocking Date but I have had success with it.

@github-actions
Copy link

This issue is stale because it has been open for 1 year with no activity. Remove stale label or comment or this will be closed in 14 days.

@github-actions github-actions bot added the Stale label Feb 25, 2022
@github-actions
Copy link

This issue was closed because it has been stalled for 7 days with no activity. Please open a new issue if the issue is still relevant, linking to this one.

@github-actions
Copy link

github-actions bot commented May 3, 2022

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 3, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

3 participants