TypeError:试图将未定义的属性文本包装为函数



我有一个javascript项目,它导入jsPDF库

pdf.js:的内容

import { jsPDF } from "jspdf";
createPDF (transcriptString) {
const transcriptPdfDoc = new jsPDF()
transcriptPdfDoc.text(transcriptString, 10, 10)
transcriptPdfDoc.save('test.pdf')
}

pdf.spec.js:的内容

import { jsPDF } from "jspdf";
beforeEach(() => {
sandbox = sinon.sandbox.create()
})
afterEach(() => {
sandbox.restore()
})

describe('createPDF', () => {
it('should save transcript PDF', () => {
const transcriptString = 'test transcript string'
const jsPdfStub = sandbox.spy(jsPDF.prototype, 'text')

//This function is working OK here in karma 
pdf.createPDF(transcriptString)

expect(jsPdfStub).to.have.been.called
})
})

上面的单元测试代码给了我这个错误:TypeError:试图将未定义的属性文本包装为函数。

很直接:原型不包含名为text的函数,因此出现了错误。我写了这个快速脚本来验证:

import { jsPDF } from "jspdf";
console.dir(jsPDF);
console.log("prototype", jsPDF.prototype);
console.log("prototype has text?", typeof jsPDF.prototype.text !== "undefined");
const pdf = new jsPDF();
console.log(pdf.text);

哪个输出这个

$ node test.js 
[Function: I] {
API: {
events: [
[Array], [Array],
....
....
RadioButton: [Function: ft],
CheckBox: [Function: pt],
TextField: [Function: gt],
PasswordField: [Function: mt],
Appearance: {
CheckBox: [Object],
RadioButton: [Object],
createDefaultAppearanceStream: [Function: createDefaultAppearanceStream],
internal: [Object]
}
},
getPageSize: [Function (anonymous)],
__bidiEngine__: [Function (anonymous)]
}
prototype { __bidiEngine__: [Function (anonymous)] }
prototype has text? false
[Function (anonymous)]

看了一下源代码,基本上没有添加到jsPdf的原型中。当您通过调用构造函数创建新对象时,所有插件和API方法都被添加/绑定到实例

侦察text方法

您不需要试图在原型上找到方法,而只需监视实例方法。

const pdf = new jsPDF();
const spy = sinon.spy(pdf, "text");
const transcriptString = "test transcript string";
pdf.text(transcriptString, 10, 10);
console.log("called?", spy.called); // ==> true

在您使用自己的createPDF()函数的示例中,这将不起作用,因为您在闭包中屏蔽了实例的访问权限。您需要以某种方式连接到实例创建过程中。有几种方法可以";"修复";这一种方法是提取一个工厂函数来创建jsPDF实例和一个相关的setter,该setter允许您替换被调用的构造函数。另一种方式可以是使用";连接接缝";以更换jspdf模块。

使用连接缝(模块更换(

看起来像这样:

import {jsPDF} from 'jspdf';
// later ... in your test
let createdInstance;
let spy;
const createPdf = proxyquire("../src/createPdf", {
jspdf: {
jsPDF: (...args) => {
createdInstance = new jsPDF(...args);
sinon.spy(createdInstance, 'text');
return createdInstance;     
});
}
});
pdf.createPDF(transcriptString)

expect(spy).to.have.been.called

这样做的优点是不需要接触或重构原始代码,但它是非常特定于环境的。proxyquire只能在Node中工作,您需要Webpack的其他东西,Vite、Jest等的不同解决方案。

"手动;DI

如果你稍微重组一下代码,无论你使用什么环境或框架,它都会让测试变得更容易:

export class PdfCreator(){
constructor(opts) {
this.jsPDF = opts?.stubs?.jsPDF || jsPDF;
}
createPdf(text){
const transcriptPdfDoc = new this.jsPDF()
transcriptPdfDoc.text(transcriptString, 10, 10)
transcriptPdfDoc.save('test.pdf')
}
}
const defaultInstance = new PdfCreator();
export default createPdf = (text) => defaultInstance.createPdf();

通过这种方法,您可以完全控制创建过程。如果你想测试pdf的创建,你可以创建一个creator = new PdfCreator({stubs: {jsPDF:spy}});并调用creator.createPdf('foo')来测试它

最新更新