我有一个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')
来测试它