Testing typescript modules without exports - unit-testing

I'm working on legacy environment and I have Typescript files which contain modules or/and classes
class SomeClass {
...
}
module AndAModule {
export namespace NestedNamespace {
...
}
}
Notice the lack of "export" keyword in the top level modules/classes.
I would like to test functions in the "NestedNamespace" using Jest library. However when I import the Typescript file in a test file:
var service = require("/path/to/file/SomeService.ts")
I have no access neither to a class nor module defined in the SomeService.ts file. If add the "export" keyword before module and class I'm able to access them through the object returned from require, however this breaks whole environment and other files, as after this, the module and the class are not present in the outFile generated by typescript.
How can I import desired modules from files, without adding any export statement?

If you have a function which is not being exported, then some other function which is exported, is calling that un-exported function. Test the exported method, and you will indirectly be testing the un-exported one.

Related

Ensuring a sandboxed mock refers to the same module instance as the Jest environment already has loaded

This is a bit hairy, so bare with me for a second :) Our Jest environment, custom-env, requires a module A and sets adds a function to the global scope using this.global.getBar = A.getBar.
The custom environment is used by a test. This test has a dependency to another module B, which is mocked out using jest.mock('../../moduleB', () => require('./FakeModuleB')). FakeModuleB is using the methods on module A.
Module A looks like this:
const bar = generateUuid();
module.exports = { getBar(){ return bar; } }
The weird thing here is that what is returned by getBar() differs depending on whether or not it is loaded through jest.mock() or loaded by the Jest environment. Essentially, jest.mock() seems to have cleared the loading cache somehow, so it loads the module afresh, which makes me end up with two instances of the module.
I am wondering what the "right" way of handling this could be?
The hackish way is of course to realize what this is and expose a reference to the loaded module on the global scope and refer to that in the require calls in ./FakeModuleB (which is loaded by jest.mock()):
// import { getBar } from '../../A'
const { getBar } = global.aModule;
I have then also exposed them in the jest env setup() method:
const { getBar } = aModule
this.global.aModule = aModule
this.global.getBar = getBar
Of course, I do not like this approach, polluting the global namespace for the sake of hacking around loader issues, but I am not sure how to approach this in another way.

Mock not being called in Jest

Please help me understand Jest mocks.
I've put some dummy functions in a file:
// actions.js
export function f1() {
return 1
}
export function calls_f1() {
f1()
}
And then in my test file I'm trying to understand how to check that a function calls another function:
import * as actions from './actions.js'
describe("MOCKS", () => {
actions.f1 = jest.fn();
actions.calls_f1();
expect(actions.f1).toBeCalled();
});
But the test is failing saying the mock function wasn't called. I've also tried swapping the 2nd and 3rd lines of the test, to no avail.
My jest config is all good, I've actually been doing a bunch of other testing (in this same file) that works.
What am I missing here?
Note: The actual implementation of this (that I'm simplifying greatly here) involves an actions file that includes a public export function fetchStations() that calls a private (or, rather, not exported) export function _downloadStations(). I'm trying to test that _downloadStations() is called.
I'm using import * as actions only for convenience, so I can write that line and then use whatever functions that file exports under actions.whatever() (instead of having to add functions to the import statement when I decide to use them). If import * as actions has some effect I'm not noticing (as implied by brian below) then I certainly don't have to use it and can use import {thisAction, thatAction} from './actions' of course.
This line:
import * as actions from './actions.js'
binds the module exports from actions.js to actions...
...so setting actions.f1 to a mock function replaces the module export for f1...
...but this doesn't affect calls_f1 since it calls f1 directly.
If calls_f1 is changed to call the module export for f1 then it will call the mock function.
There are two ways to make that happen.
One way is to move f1 into its own module.
The other way is to note that ES6 modules "support cyclic dependencies automatically" (a major design goal of ES6 modules) so a module can import its own exports:
actions.js
import * as actions from './actions'; // <= import the module...
export function f1() {
return 1
}
export function calls_f1() {
actions.f1() // <= ...and use it to call f1
}
actions.test.js
import * as actions from './actions.js'
describe('actions', () => {
it('calls_f1 should call f1', () => {
actions.f1 = jest.fn();
actions.calls_f1();
expect(actions.f1).toBeCalled(); // Success!
})
})
Update
OP updated the question to indicate that the function in question is not exported from the module.
In that case it is just an internal implementation detail of the module and cannot be spied on directly.
Testing it would involve testing for the effects that it causes, and not directly spying on it to see if it was called.

Using a type as a value in typescript

I'm using inject-loader to mock dependencies for a Typescript project being unit tested. The service I'm testing has an import line like this:
import pnp, { SearchQuery, Sort, SortDirection, CamlQuery } from "sp-pnp-js";
For my test, I want to mock several of the functions on pnp, but keep the classes intact. In my unit test file, I've included this import:
import { SearchQuery, Sort, SortDirection, CamlQuery } from "sp-pnp-js";
Which gives my test access to the necessary classes. I've declared a mock service object:
// mock services
const mockPnp = {
default: { ... }
};
Which I'm wiring into my target class instance:
Service = require("inject!./sharepointservice")({
"sp-pnp-js": mockPnp
}).Service;
And everything works, so long as I don't run any code that references those classes (ie. SearchQuery). To get that to work, I tried adding it to the mock:
// mock services
const mockPnp = {
default: { ... },
SearchQuery: SearchQuery
};
However, I get a 'SearchQuery' only refers to a type, but is being used as a value here error.
I've tried casting to any (same error), tricks with modules and exports, with no luck. I'm supposed to be able to write any Javascript with Typescript, and this would work trivially in Javascript - what am I missing here?
According to the definition file SearchQuery is an interface, which means that you can not treat it as a value (as the error message says).
Typescript interfaces aren't being compiled into the js output, and you can not use them at runtime.
Technically, since its just for type safety, I can do this
t: MyInterface = {};

How can I use sinon to spy on a function that is imported into an ember-cli app?

I have an ember app built using ember-cli conventions and I would like to spy on a util function using sinon. The problem is I don't know how to reference the object that the function is defined on.
I have a function defined as follows in app/utils/report-error.js:
reportError = function(err) {
# ...
};
export default reportError;
And then I import that function elsewhere as follows:
import reportError from '../utils/report-error';
Is it possible to spy on this function using sinon? Normally I would refer to the object on which the function is defined:
sandbox = sinon.sandbox.create();
sandbox.spy(someObject, "reportError");
But what is the someObject of a bare function that gets imported?
I realize I could just put the function on an object and import the object, but that means that everywhere the function is used, I need to reference the receiver object too, which seems messy.
When the I look at the javascript that is produced after the import and export statements are transpiled, there is actually an object with a function on it:
reportError['default']();
But any mentions to reportError get similarly transpiled and there doesn't seem a way to refer to the host object of the dependency.
Thoughts?

creating TypeScript definitions for existing jslibraries nameclash issues

I have started making TypeScript definitions for the Ember.js framework. I have currently set it up structured like this:
declare module Ember {
interface Classname {
someProperty: type;
}
declare var Classname: Classname;
}
In order to access these interfaces I declare a variable. However in my library there is a classname called Object, this causes a nameclash with the Object from the global scope.
How do I surpass this nameclash? And am I using the right practise for creating definitions for an existing library?
You might want to grab the EmberJS definition file from GitHub:
https://github.com/borisyankov/DefinitelyTyped/blob/master/ember/ember-1.0.d.ts
This not only solves your problem as it has the Ember definition, but you can also see how they achieved it in spite of name clashes:
declare module Ember {
export class Object extends CoreObject {
//...
This makes Object a child of Ember, rather than of window:
Ember.Object
Rather than
window.Object
// or the shorthand
Object // which is window.Object