Why jest config's unmockedModulePathPatterns does not unmock axios? - unit-testing

I have an application where I am trying to differentiate integration tests from unit tests. This difference is made through two types of files suffixes .ispec.ts for integration and .test.ts for unit.
Another difference between them is that unit tests shouldn't access the network, for that I've setup a mock for axios on <rootDir>/__mocks__/axios.ts which prevents that behavior successfully. For integration tests however, I'd like to go over the network and axios should keep it's normal operation.
To prevent axios from being mocked on ispec files, I am adding the following line in all the files:
// anyFile.ispec.ts
imports (...)
jest.unmock('axios'); // this line required in every .ispec file
describe(...)
Reading the jest documentation, I came across the following option unmockedModulePathPatterns where as the name suggests, defined modules on this array will be unmocked by the jest cli.
So I've changed my jest configurations for:
// jest.unit.js
module.exports = {
testRegex: '/__tests__/.+\\.test\\.ts$'
};
and
// jest.integration.js
module.exports = {
testRegex: '/__tests__/.+\\.ispec\\.ts$',
unmockedModulePathPatterns: ['axios']
}
To my surprise, after removing the line jest.unmock('axios') from my ispec files and running jest --config ./jest.integration.js still makes use of the mocked axios which is not my intention.
Did I miss the point of unmockedModulePathPatterns or is it a bug on jest configuration? I also tried setting automock to false, but the behavior is still the same.

Related

Mocking PDFtron webviewer with jest in react application

Hi I am new to unit testing and I am trying to mock the 'annotationManager' of the PDFtron webviewer
I am using this below code in my test file
jest.mock('#pdftron/webviewer', () =>({
annotationManager: {
getAnnotationsList: jest.fn().mockReturnValue([]),
deleteAnnotations: jest.fn()
},
}));
In the code, I'm getting the list of annotations using 'getAnnotationsList' function and deleting it using 'deleteAnnotations' function.
In the log of the unit tests, I'm getting this following error
'cannot read the properties of undefined (reading 'getAnnotationsList')
Is this the correct way to doing things or am I missing something?
are you able to share an example of a test you are writing where you need to mock the annotation manager? Depending on how you are using the WebViewer package, mocking the annotation manager can be different. If you prefer to reach out to Apryse directly you can also reach out to them via their support form

jest manual ES6 class mock is not active and I want to understand why

I am having problems using Jest manual mocks (the one in a parallel __mocks__ directory) in my project.
I think I understand how to use it and it actually works fine if I remove a single line in a file specified in the Jest setupFiles array.
In that file a global helper is installed (into global.createComp) that uses the vuex store.
This is a vue + vuex project but even running the stripped down spec using only jest gives unexpected results.
Can somebody look at my minimal reproducible example repo at https://github.com/thenoseman/jest-manual-mock-not-working, do a npm i and npm run test:unit and help me understand why the mock is not active?
You can find the line that need to be commented out in test/unit/support/helpers.js.
Also the README shows a screenshot and further explains what the problem looks like.
setupFiles are evaluated before test files. As the reference states,
A list of paths to modules that run some code to configure or set up the testing environment. Each setupFile will be run once per test file. Since every test runs in its own environment, these scripts will be executed in the testing environment immediately before executing the test code itself.
JavaScript modules are evaluated once on first import. Importing #/store/modules/internetAtHome in helpers.js results in importing original #/api/DslService.
The mock in test file doesn't affect #/api/DslService because it has already been evaluated earlier:
jest.mock("#/api/DslService");
import DslService from "#/api/DslService";
In case helpers.js needs mocked #/api/DslService, jest.mock needs to be moved there.
In case helpers.js needs original #/api/DslService but tests need mocked one, the module (and any module that depends on it) needs to be re-imported with jest.resetModules or jest.isolatedModules:
jest.mock('#/api/DslService');
let DslService;
jest.isolateModules(() => {
DslService = require("#/api/DslService").default;
});
...
For a module that was imported with original implementation and needs to be re-imported as a mock, jest.requireMock can be used, it doesn't need jest.mock('#/api/DslService'):
let DslService = jest.requireMock("#/api/DslService").default;
...

ReferenceError: ga is not defined [Ionic 2.2 Unit Testing With Karma]

I'm adding unit tests to an Ionic 2.2.0 app I manage, but my Components crash at test-time when they encounter Google Analytics code. I'm using Ionic's official unit testing example as a basis, and my current progress can be seen on our public repo.
My project uses Google Analytics, which is added to the HTML and downloaded at runtime (because we have different keys for development vs production).
The code that initializes Analytics is in my main.ts, and it sets a global variable ga, which is subsequently available throughout the application.
I'm beginning the tests for the app's first page, which uses Analytics. When I run the tests, I'm met with the following error
Component should be created FAILED
ReferenceError: ga is not defined
at new MyBusesComponent (webpack:///src/pages/my-buses/my-buses.component.ts:33:6 <- karma-test-shim.js:138419:9)
at new Wrapper_MyBusesComponent (/DynamicTestModule/MyBusesComponent/wrapper.ngfactory.js:7:18)
at CompiledTemplate.proxyViewClass.View_MyBusesComponent_Host0.createInternal (/DynamicTestModule/MyBusesComponent/host.ngfactory.js:15:32)
........
This is because main.ts doesn't seem to be loaded or executed, and I assume TestBed is doing that purposefully. It's certainly better that I don't have the actual Google Analytics object, but the Component does need a function called ga.
My question, therefore, is as follows: how can I create Google Analytics' ga variable in my test configuration such that it's passed through to my components at test-time?
I've tried exporting a function from my mocks file and adding it to either the imports or providers arrays in my spec file, but to no avail.
I appreciate any advice! Feel free to check my code at our repo I linked to above and ask any followups you need. Thanks!
You declare the var ga but that is just to make TypeScript happy. At runtime, the ga is made global from some external script. But this script is not included in the test.
What you could do is just add the (mock) function to the window for the tests. You could probably do this in your karma-test-shim.js.
window.ga = function() {}
Or if you wanted to test that the component is calling the function with the correct arguments, you could just add the function separately in each test that uses the function. For example
beforeEach(() => {
(<any>window).ga = jasmine.createSpy('ga');
});
afterEach(() => {
(<any>window).ga = undefined;
})
Then in your test
it('..', () => {
const fixture = TestBed.creatComponent(MyBusesComponent);
expect(window.ga.calls.allArgs()).toEqual([
['set', 'page', '/my-buses.html'],
['send', 'pageview']
]);
})
Since you're making multiple calls to ga in the constructor, the Spy.calls will get the argument of all each call and put them in separate arrays.

Unit test a polymer web component that uses firebase

I have been trying to configure offline unit tests for polymer web components that use the latest release of Firebase distributed database. Some of my tests are passing, but others—that look nigh identical to passing ones—are not running properly.
I have set up a project on github that demonstrates my configuration, and I'll provide some more commentary below.
Sample:
https://github.com/doctor-g/wct-firebase-demo
In that project, there are two suites of tests that work fine. The simplest is offline-test, which doesn't use web components at all. It simply shows that it's possible to use the firebase database's offline mode to run some unit tests. The heart of this trick is the in the suiteSetup method shown below—a trick I picked up from nfarina's work on firebase-server.
suiteSetup(function() {
app = firebase.initializeApp({
apiKey: 'fake',
authDomain: 'fake',
databaseURL: 'https://fakeserver.firebaseio.com',
storageBucket: 'fake'
});
db = app.database();
db.goOffline();
});
All the tests in offline-test pass.
The next suite is wct-firebase-demo-app_test.html, which test the eponymous web component. This suite contains a series of unit tests that are set up like offline-test and that pass. Following the idea of dependency injection, the wct-firebase-demo-app component has a database attribute into which is passed the firebase database reference, and this is used to make all the firebase calls. Here's an example from the suite:
test('offline set string from web component attribute', function(done) {
element.database = db;
element.database.ref('foo').set('bar');
element.database.ref('foo').once('value', function(snapshot) {
assert.equal(snapshot.val(), 'bar');
done();
});
});
I have some very simple methods in the component as well, in my attempt to triangulate toward the broken pieces I'll talk about in a moment. Suffice it to say that this test passes:
test('offline push string from web component function', function(done) {
element.database = db;
let resultRef = element.pushIt('foo', 'bar');
element.database.ref('foo').once('value', function(snapshot) {
assert.equal(snapshot.val()[resultRef.key], 'bar');
done();
});
});
and is backed by this implementation in wct-firebase-demo-app:
pushIt: function(at, value) {
return this.database.ref(at).push(value);
},
Once again, these all pass. Now we get to the real quandary. There's a suite of tests for another element, x-element, which has a method pushData:
pushData: function(at, data) {
this.database.ref(at).push(data);
}
The test for this method is the only test in its suite:
test('pushData has an effect', function(done) {
element.database = db;
element.pushData('foo', 'xyz');
db.ref('foo').once('value', function(snapshot) {
expect(snapshot.val()).not.to.be.empty;
done();
});
});
This test does not pass. While this test is running, the console comes up with an error message:
Your API key is invalid, please check you have copied it correctly.
By setting some breakpoints and walking through the execution, it seems to me that this error comes up after the call to once but before the callback is triggered. Note, again, this doesn't happen with the same test structure described above that's in wct-firebase-demo-app.
That's where I'm stuck. Why do offline-test and wct-firebase-demo-app_test suites work fine, but I get this API key error in x-element_test? The only other clue I have is that if I copy in a valid API key into my initializeApp configuration, then I get a test timeout instead.
UPDATE:
Here is a (patched-together) image of my console log when running the tests.:
To illustrate the issue brought up by tony19 below, here's the console log with just pushData has an effect in x-element_test commented out:
The offline-test results are apparently false positives. If you check the Chrome console, offline-test actually throws the same error:
The error doesn't affect the test results most likely because the API key validation occurs asynchronously after the test has already completed. If you could somehow hook into that validation, you'd be able to to catch the error in your tests.
Commenting out all tests except for offline firebase is ok shows the error still occurring, which points to suiteSetup(). Narrowing the problem down further by commenting 2 of the 3 function calls in the setup, we'll see the error is caused by the call to firebase.initializeApp() (and not necessarily related to once() as you had suspected).
One workaround to consider is wrapping the Firebase library in a class/interface, and mocking that for unit tests.

Cannot access modules with ng-mock in angular unit tests

I am using Karma and Jasmine for my unit tests. However, I am unable to mock or acquire the modules in my unit tests. I keep getting the same error
Error: [$injector:modulerr] Failed to instantiate module ha.module.core due to:
Error: [$injector:nomod] Module 'duScroll' is not available! You either
misspelled the module name or forgot to load it. If registering a module
ensure that you specify the dependencies as the second argument.
However, I am injecting the modules in the beforeEach function on top
angular.mock.module('duScroll');
angular.mock.module('ui.router');
angular.mock.module('ha.module.utility');
angular.mock.module('ha.module.core');
In my karma.config file I am requiring the js files
files: [
'../node_modules/jquery/dist/jquery.js',
'../node_modules/angular/angular.js',
'../node_modules/angular-mocks/angular-mocks.js',
'src/modules/utility/module.utility.built.js',
'src/modules/utility/services/*.js',
'src/modules/core/module.core.built.js',
'src/modules/**/**/*.js',
'src/modules/core/**/*.js',
'../Templates/**/*.html',
'tests2/**/*.js',
],
I have attached a screen shot to show what is showing up when I run karma. I am getting the files to show up in my developer tools.
All I can wonder is if there is something else additional that I must do to get the modules initiated to run the tests? The files are loaded after angular and angular mocks and they are loaded before my testing folder. Not sure why I cannot get the modules though.
You do not need to call angular.mock.module, it is enough to call beforeAll(module('<module-name>')) for each module you want to mock.
Then, to inject your controllers afterwards, you have to use
beforeEach(inject(function(_$controller_){
// The injector unwraps the underscores (_) from around the parameter names when matching
$controller = _$controller_;
}));
Inforation from https://docs.angularjs.org/guide/unit-testing#testing-a-controller. Please visit it for more info.