У меня есть функция с переменным функционалом, основанная на файле, который она читает, который управляется через Map
, который он хранит в памяти:
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
и f3
все делают намного больше, чем показано здесь, и каждый требует большого количества макетов для успешного тестирования.
По мере того, как код становится все более развитым и варианты использования продолжайте, будет произвольное количество функций, которые, возможно, потребуется вызвать, основываясь на расширяющемся наборе входных данных. doThing()
является сложным и берет информацию из множества различных источников, включая как содержимое данного файла, так и базу данных, что помогает ему выбрать, какой файл выполнять. С точки зрения клиента, doThing()
- единственная функция, о которой он заботится. Таким образом, это единственный файл export
, редактируемый этим файлом.
Я пытаюсь протестировать механизм в doThing()
, который определяет, какой key
он должен использовать. Я не хочу издеваться над f1
, f2
и f3
конкретно - я хочу представить еще много опций, на которые указывают другие вещи, над которыми я издеваюсь doThing()
. Однако, чтобы проверить, вызывает ли он правильный фальшивый метод, мне нужно выяснить, какой фальшивый метод он вызывает. Мое попытанное решение использует приведение типов, чтобы вытащить приватное myMap
из файла, а затем шпионить за его get()
методом:
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();
});
Однако в аннотированной строке выше появляется сообщение об ошибке: Error: <spyOn> : could not find an object to spy upon for get()
. После дальнейшего исследования (то есть пошагового отладчика) выясняется, что импортированный мной file1
объект имеет только doThing()
и не имеет никаких других своих частных переменных.
Как мне успешно смоделировать преобразование ключ-значение здесь - что означает, в данном случае, слежку за атрибутами частной переменной, чтобы я мог получить своих шпионов в нужном месте? Можно либо полностью заменить myMap
, либо заменить myMap.get()
, если это возможно.