My code is like below...
describe('The Home Page',() =>{
before(()=>{
cy.fixture('WorkHours').then(function(data){
this.xxx = data
cy.log(this.xxx.weekDate+'..One')
})
})
it( 'checkNavis',() =>{
cy.log(this.xxx.weekDate+'..two')
cy.visit('/')
Gets an error ....[Cypress Log]
why dose it not referring to global variable?
could not be synchronization problem since waiting for promises??
since xxx is a declared global, shouldn't be scope??
this is related but doesn't give me a clue to identify problem...
Cypress - Typescript - How can i make my fixture variables global?
In your tests or hooks you cannot access aliases using this.* using arrow functions.
This is most likely because when this is used in an arrow functions it will have the same value as the context in which the arrow function was created (ie. undefined). The classic function () has its own this which is has global access.
Another example of this behaviour is in mocha. It has a restriction on the use of arrow functions.
Instead try:
describe('The Home Page',() =>{
before(function() {
cy.fixture('WorkHours').then(function(data){
this.xxx = data
cy.log(this.xxx.weekDate+'..One')
})
})
it('checkNavis', function() {
cy.log(this.xxx.weekDate + '..two')
cy.visit('/')
Related
I hope someone might help me understanding the interactivity of js prototypes and jest.spOn().
I have a small Example:
An example class in file TestObj.ts:
export default class TestObj {
foo() {
// Do Something e.g.
console.log("Hello World!");
}
}
The following example Test Case is succeeding but the console.log is never executed.
import TestObj from './TestObj';
const spyObj = jest.spyOn(TestObj.prototype, 'foo');
test('debug test', () => {
const obj = new TestObj();
obj.foo();
expect(spyObj).toHaveBeenCalled();
});
If I change the Example Test Case to the following, the test succeeds and the console.log statement is called as expected.
import TestObj from './TestObj';
test('debug test', () => {
const spyObj = jest.spyOn(TestObj.prototype, 'foo');
const obj = new TestObj();
obj.foo();
expect(spyObj).toHaveBeenCalled();
});
Any idea why the version, using the global spyOn variable does not work as expected?
Edit:
It seems to be not related to prototypes.
The same Issue is there for a function without any kind of class,
editing the First Code snippet (TestObj.ts) to this:
export foo() {
// Do Something e.g.
console.log("Hello World!");
};
We receve the same issue for the updated second snipped. (The test succeeds but the console log is never reached.)
import * as testlib from './TestObj';
const spyObj = jest.spyOn(testlib, 'foo');
test('debug test', () => {
testlib.foo();
expect(spyObj).toHaveBeenCalled();
});
However if we update the second snippet to the following the test succeeds and the console log is executed:
import * as testlib from './TestObj';
const spyObj: jest.SpyInstance;
beforeEach(() => {
spyObj = jest.spyOn(testlib, 'foo');
});
test('debug test', () => {
testlib.foo();
expect(spyObj).toHaveBeenCalled();
});
However I have still no clue why I discover this issue.
who ever comes across this post,
Problem explanation
I did a lot more of research(try&error, mdn, jest manpage and a lot of medium articles) and I guess that I found out the reason for the strange behavior. To understand this issue it is important to know a number of points:
1: JS prototypes are global variables, every object of the corresponding type relies on.
2: Jest does not reset global variables after every test, changes made before, or inside any test to a global variable will be kept for the whole test suite (file).
3: The jest spy on function is actually a mockup of the specified function with an implementation calling the function it self. e.g.:
jest.SpyOn(TestObj.prototype, 'foo'); actually is implemented as: TestObj.prototype.foo = new jest.fn().mockImplementation(()=>{original_TestObj.prototype.foo()});
This means spying on a function of a class prototype is actually changing a global variable.
4: Depending on your jest config, there is the possibility to reset mockup functions before every test to the default value. But be careful the default function for spyOn seems to be the same as for jest.fn() it self, an empty implementation, which means the mock-up is still callable but no code, especially not the original implementation is executed.
Solution
avoid changing global variables, if you want your testcases independent from each other.
avoid spying on prototypes, in test cases, if you require the spy only in a single test try to spy on the local object eg:
test('should foo', () => {
const testObj = new TestObj();
const spyOnFn = jest.spyOn(testObj, 'foo');
// Do anything
expect(spyOnFn).to//Have been anything
});
if requiring spyOn implementations of the same function
in more than one test try to create a global variable for the Test
but use the before each functionality of jest to setup the Spy. This
functionality is executed after the reset of all mocks (if enabled).
e.g.:
let spyOnFunction1: jest.SpyInstance;
beforeEach(()=> {
spyOnFunction1 = jest.spyOn(TestObj.prototype, 'foo');
});
I am trying to reuse a variable between multiple jest.mock calls in a same test file.
jest.mock calls are hoisted to the top by babel-jest so usually the variables outside the mock function scope are not allowed to be accessed. But the error message says:
This is a precaution to guard against uninitialized mock variables. If it is ensured that the mock is required lazily, variable names prefixed with mock are permitted.
So If I prefix the variables with mock, I no longer receive the error but the values are all still set to undefined inside the mock. I don't understand what the error message means by "lazily required". Is it possible at all to share variables between jest.mock calls?
Reproducable example:
const mockReusableValue = { x: 5 };
jest.mock('../someModule', () => {
console.log(mockReusableValue); // undefined
return {};
});
babel-jest will hoist the module mock on the top of the scope meaning you can define the mock-prefixed variable in the outer scope and reuse it in your module mock like this
test.js
const mockData = 3;
describe('someModule`s someMethod returning mockData', () => {
jest.mock('./someModule', () => ({
someMethod: jest.fn().mockReturnValue(mockData)
}))
})
working example
I'm using Meteor and am writing unit tests for a Collection. I've got Helper methods for the collection in addition to just regular JS functions.
I.e.
Collection.helpers({
helperFn: function () {
return 'foo';
}
});
//And in the same file
function bar() {
return "bar";
}
Then in my tests file I have something like
import { Collection } from '../collections'
//Use Factory or Stub to create test Document
//This then works just fine and I can assert, etc..
testDoc.helperFn
My question is with wanting to test just the regular 'bar' JS function. This isn't a big deal using ES6 classes because then I can just export the whole class and call any function with an instance of it. But with Meteor I'm finding the only way I can access the function is by using the 'export' keyword.
So in my Collection file
export function bar ({ return bar; });
And now in my test file I'd do something like
import { bar } from '../collection'
I'd rather not add an export statement for every time I test a new function. Is there any way around this or is it not a big deal?
I do think that the export/import is the way to go, but to answer the first part of your question: yes, you can fall back to the original scoping of meteor and put these functions in the global scope of meteor as follows:
do not put your files in the imports/ folder, but into another folder in your project, e.g., server/.
defined the functions as:
bar = function() { /* function body */ }
These variables are interpreted by meteor as being global to the project, and hence do not need to be imported before use.
That said, there was a reason meteor introduced the imports/ folder and corresponding export/import paradigm in version 1.3. It avoids polluting the global scope and makes it much easier to see where things are defined.
I'm trying to have full test coverage on some helper functions in my project. My first tests for either of the functions checks whether 2 out-of-scope variables are declared or not, which run successfully.
What I want to do is, jest to set/mock wrapper and layer variables declared out-of-scope of the functions to be tested, but can't seem to have it right no matter how hard I tried.
Thankful and appreciate any kind of help in this direction as I'm fairly rookie in terms of writing tests in general not to mention tests for jest.
I have tried jest.mock by mocking by default my imported module and then did jest.requireActual on 2 functions.
I have tried jest.doMock with a module factory which sets these 2 variables and then returns in an object.
Module: helpers/index.js
let wrapper; // Is created on runtime ergo initially undefined.
const layer = document.createElement('div');
layer.classList.add('layer');
const isElement = elm => {
if (!(elm instanceof Element)) {
throw new Error('Given element is not of type Element or is undefined!');
}
}
export const someFn = async () => {
wrapper = document.querySelector('.wrapper');
isElement(wrapper);
isElement(layer);
// Code that needs to be tested
// but cannot since when testing this function
// 'wrapper' and 'layer' are always undefined.
}
Test: helpers/index.test.js
// Helpers
import {someFn} from 'helpers';
describe('Method: someFn', () => {
test('should NOT throw', async () => {
await expect(someFn()).resolves.not.toThrow(); // but it does!
});
});
Actual result: Received promise rejected instead of resolved
Expected result: Not to throw, considering these out-of-scope variables that this function relies on, can somehow be set/mocked while testing it.
Why should we not just replace beforeAll(function(){ let foo = 'test' }) with let foo = 'test' ? What's the purpose of beforeAll if the second way is fine?
Here's the official definition by the way: The beforeAll function executes once and only once for the describe block containing it, before any of the beforeEach functions or any of the specs.
There are some substantial differences between beforeAll block and plain code in the scope of describe function.
beforeAll setup is supposed to be coupled with afterAll teardown.
Errors in before* blocks are not critical and won't prevent the list of specs from being populated and specs from being run. An error in describe block will result in empty list of specs.
before* and it blocks have this as the context of current spec which can be used as recommended way to share data between blocks. It is generally more preferable than plain variables from parent scope because there's no risk to cross-contaminate tests this way. describe function doesn't have this context. However, this approach isn't compatible with ES6 arrow functions.
There may be Jasmine helpers that are supposed to be work in conjunction with before* and it blocks, e.g. in Angular testing:
beforeAll(fakeAsync(() => {
asyncInitializationThatDoesntNeedDoneCallback();
}));
If it is a constant that should be defined for the entire block, then it can surely be defined without beforeAll:
describe('...', () => {
const foo = 'test'; // not let
...
});