mockRestore does not clear a jest.spyOn().mockImplementation mock - testing

I am mocking Date.now() implementation returning a specific date however, after the test is done the afterAll + mockRestore() doesn't quite rid of the mock.
When I run another test the date now is still mocked to 1626764400000. Is there a different function I have to use to reset the mock? I have already used: mockReset, mockClear, jest.clearAllMocks.
beforeAll((): void => {
jest.spyOn(Date, 'now').mockImplementation(() => 1626764400000);
});
afterAll((): void => {
jest.clearAllMocks();
jest.spyOn(Date, 'now').mockRestore();
});

I have solved a similar issue by calling mockRestore of the returned value of the jest.spyOn(), that also returns a Jest mock function.
Oficcial docs.

Related

Unit testing asynchronous call in angular

I have following function in a service 'MyService':
myFunction(input: MyInput){
this.someService.someFunction(<any> input).then(result => {
console.log('Somefunction worked successfully');
return result;
}).catch(reason => {
throw new Error('Somefunction failed');
})
}
And unit test for myFunction is as:
// create test data
spyOn(someService, 'someFunction').and.returnValue(Promise.resolve(dummyResult));
// invoke target unit
const response = myService.myFunction(dummyInput);
// validations
expect(someService.someFunction).toHaveBeenCalled();
expect(someService.someFunction).toHaveBeenCalledTimes(1);
expect(someService.someFunction).toHaveBeenCalledWith(dummyInput);
expect(response).toBe(dummyResult)
First three validations pass, but last one doesn't as call to someFunction is asynchronous and hence response is still undefined at time of validation.
How to handle such scenario?
I came across fakeAsync but examples are a bit different from this.

Jest spyOn vs mock - both defined and undefined errors

Just upgraded to jsdom-fourteen in my jest configuration. It's working wonderfully, but a single test is failing.
test('Do the thing', () => {
window.location.assign = jest.fn();
});
I inherited this code. It looks like a simple enough jest mock. It complains that it cannot assign the read-only property assign and that makes sense, I assume this is jsdom functionality that was added.
However... I can't do a jest.spyOn either, which seems to be what is suggested. I've not used spyOn before.
jest.spyOn(window.location.assign);
But this gives me an undefined property error:
Cannot spy the undefined property because it is not a function; undefined given instead
The line before this, I added a log just to check. It is definitely a function:
console.log(window.location.assign);
=> [Function: assign]
I'm not sure how these two errors can even coexist - both defined and undefined?
Due to how JavaScript works, it would be impossible to write spyOn function the way that allowed it to work like spyOn(window.location.assign). Inside spyOn, it's possible to retrieve window.location.assign function that was provided as an argument but not window.location object and assign method name to do window.location.assign = jest.fn().
The signature of spyOn is:
jest.spyOn(object, methodName)
It should be:
jest.spyOn(window.location, 'assign');
This may be unworkable as well because window.location.assign is read-only in later JSDOM versions, which is used by Jest to emulate DOM in Node.js. The error confirms that this is the issue.
It may be possible to mock read-only property manually:
const origAssign = Object.getOwnPropertyDescriptor(window.location, 'assign');
beforeEach(() => {
Object.defineProperty(window.location, 'assign', { value: jest.fn() })
});
afterEach(() => {
Object.defineProperty(window.location, 'assign', origAssign)
});
This wouldn't work with real DOM because built-ins may be read-only and non-configurable. This is the issue in Chrome. For testability reasons it may be beneficial to use location.href instead of location.assign.
Eventually worked through some things and found this:
delete global.window.location;
window.location = { assign : jest.fn()};
As it appears later iterations of jsdom lock the location object down further and further until it's completely not modifiable, #Estus' answer will only work in lower versions of jsdom/jest.

In TestCafe is there a way to know if the test passed or failed in after hook?

I am trying to mark tests as pass/failed through a rest API (Zephyr) while my testcafe tests are running. I was wondering if it's possible in the after or afterEach hook to know if the test passed/failed so that I can run some script based on the result.
Something like:
test(...)
.after(async t => {
if(testFailed === true) { callApi('my test failed'); }
})
I see two ways in which to solve your task. First, do not subscribe to the after hook, but create your own reporter or modify the existing reporter. Please refer to the following article: https://devexpress.github.io/testcafe/documentation/extending-testcafe/reporter-plugin/#implementing-the-reporter
 
The most interesting method there is reportTestDone because it accepts errs as a parameter, so you can add your custom logic there.
The second approach is using sharing variables between hooks and test code
You can write your test in the following manner:
test(`test`, async t => {
await t.click('#non-existing-element');
t.ctx.passed = true;
}).after(async t => {
if (t.ctx.passed)
throw new Error('not passed');
});
Here I am using the shared passed variable between the test code and hook. If the test fails, the variable will not be set to true, and I'll get an error in the after hook.
This can be determined from the test controller, which has more information nested within it that is only visible at run time. An array containing all the errors thrown in the test is available as follows
t.testRun.errs
If the array is populated, then the test has failed.

Reusable functions for Protractor test framework

I use Protractor Test Framework and need to use some the same operations for different cases. Like Authentication procedure.
The question is: What's the correct way to use own functions in Protractor?
I remember this tool works: 1) asynchronous and 2) its functions return promises.
Must the reusable function return the promise for make possibility do .then() operation or this function may return no value?
An Example (correct or no):
describe('Login procedure', function() {
it('Login Username', function () {
browser.get('anurl.com');
auth('username', 'password').then(function(){console.log('NICE TO MEET YOU')});
});
var auth = function(loginstr, passwordstr) {
return element(by.css('div[class="login"]')).isDisplayed().then(function (result) {
if (result) {
element(by.css('input[name="email"]')).clear().sendKeys(loginstr).then(
function () {
element(by.css('input[name="password"]')).clear().sendKeys(passwordstr).then(function () {
element(by.css('button[class="submit"]')).click().then(function () {
return element(by.id('welcome')).isPresent();
});
});
});
}
});
}
Thanks!
Your example looks fine.
It is good practice to return a promise from your utility functions (so callers can then off of it if they want). But it is not required.
In practice, the methods you are invoking that create promises (e.g., click or isDisplayed, etc) implicitly register the created promise with the control flow, so the implicit synchronization will not be impacted by refactoring the calls into your own functions.
See the Protractor Page Objects for a similar approach.
Our team uses Orchid-js with Jasmine and Protractor.
It's an extension that makes defining your own functions automatic.
In this case your code will still work, you would just be able to automatically reuse your 'Login procedure' and 'Login Username' functions as well.
Describe('Login procedure', function(username,password) {
It('Login Username', function (username,password) {
browser.get('anurl.com');
auth(username, password).then(function(){console.log('NICE TO MEET YOU')});
})(username,password);
})('username','password');
Then reuse it later
Describe('Login procedure')('differentUsername','differentPassword');

How jasmine spy example works

All;
I am just starting learning Jasmine( version 2.0.3 ), when I got to Spies section, the first example confused me:
describe("A spy", function() {
var foo, bar = null;
beforeEach(function() {
foo = {
setBar: function(value) {
bar = value;
}
};
spyOn(foo, 'setBar');
foo.setBar(123);
foo.setBar(456, 'another param');
});
it("tracks that the spy was called", function() {
expect(foo.setBar).toHaveBeenCalled();
});
it("tracks all the arguments of its calls", function() {
expect(foo.setBar).toHaveBeenCalledWith(123);
expect(foo.setBar).toHaveBeenCalledWith(456, 'another param');
});
it("stops all execution on a function", function() {
expect(bar).toBeNull();
});
});
I wonder if anyone could explain why the setBar function does not affect the bar defined inside describe block? How Jasmine spies deal with this?
Thanks
Because you are not actually executing the methods.
If you want this test to fail:
it("stops all execution on a function", function() {
expect(bar).toBeNull();
});
After these calls:
foo.setBar(123);
foo.setBar(456, 'another param');
Then you should call and.callThrough for your spy.
spyOn(foo, 'setBar').and.callThrough();
From the documentation
Spies: and.callThrough
By chaining the spy with and.callThrough, the spy will still track all
calls to it but in addition it will delegate to the actual
implementation.
With regard to your question, 'how jasmine deals with this?'
From here you can read a basic explanation:
Mocks work by implementing the proxy pattern. When you create a mock
object, it creates a proxy object that takes the place of the real
object. We can then define what methods are called and their returned
values from within our test method. Mocks can then be utilized to
retrieve run-time statistics on the spied function such as:
How many times the spied function was called.
What was the value that the function returned to the caller.
How many parameters the function was called with.
If you want all of the implementation details, you can check the Jasmine source code which is Open Source :)
In this source file CallTracker you can see how the gather data about the method calls.
A little more about the proxy pattern.