I have a function with variable functionality based on file it reads, which is controlled via a Map it keeps in memory:
file1.ts
function f1(x: number): number {
// do far-reaching things
return 1;
}
function f2(x: number): number {
// do different far-reaching things
return 2;
}
function f3(x: number): number {
// do still-different far-reaching things
return 3;
}
const myMap: Map<string, (number) => number> = new Map<string, () => void>([
['key1', f1],
['key2', f2],
['key3', f3],
]
export function doThing(filename: string): number {
// open file, make some database calls, and figure out the name of a key
// ...
let fileToExecute = myMap.get(key);
return fileToExecute(someValueDerivedFromFile);
}
f1, f2, and f3 all do much more than shown here, and each requires a lot of mocks to be tested successfully.
As the code grows more developed and use cases continue, there will be an arbitrary number of functions that might need to be called, based on an expanding set of inputs. doThing() is complicated and takes its information from a lot of different sources, including both the contents of given file and a database, which helps it choose which file to execute. From a client's point of view, doThing() is the only function it cares about. Thus, it's the only one exported by this file.
I'm trying to test the mechanism in doThing() that figures out what key it should use. I don't want to mock f1, f2, and f3 specifically - I want to present many more options, pointed to by other things I'm mocking for doThing(). However, to check if it's calling the correct fake method, I need to figure out which fake method it's calling. My attempted solution uses typecasting to try to pull the private myMap out of the file and then spy on its get() method:
file1.spec.ts
import * as file1 from '../src/file1'
...
it("calls the correct fake method", () => {
// lots of other mocks
let spies = [
jasmine.createSpy('f1spy').and.returnValue(4),
jasmine.createSpy('f2spy').and.returnValue(5),
jasmine.createSpy('f3spy').and.returnValue(6),
...
]
let mockMap = spyOn((file1 as any).myMap, 'get').and.callFake((key) => { // this fails
var spy;
switch(key) {
case 'key1': spy = spies[0]; break;
case 'key2': spy = spies[1]; break;
case 'key3': spy = spies[2]; break;
...
}
return spy;
}
result = file1.doThing(...);
expect(spies[0]).not.toHaveBeenCalled();
expect(spies[1]).toHaveBeenCalledWith(7);
expect(spies[2]).not.toHaveBeenCalled();
});
However, I get an error on the annotated line above: Error: <spyOn> : could not find an object to spy upon for get(). Upon further investigation (i.e. the step-by-step debugger), it turns out that the file1 object I imported only has doThing(), and doesn't have any of its other private variables.
How do I successfully mock the key-value transformation here - which means, in this case, spying on attributes of a private variable, so I can get my spies in the right place? Either replacing myMap entirely or replacing myMap.get() would be an option, if either is possible.