![](/img/trans.png)
[英]Testing `img.onLoad`/`img.onError` using Jest and React Testing Library
[英]How to test img.onload using Jest?
我被img.onload
事件测试困住了。 我知道这是一个异步操作,它应该被嘲笑,但我仍然无法弄清楚如何解决这个问题。 我也见过几个类似的案例,但它们与这个不同。
以前访问过:
测试代码:
function funcToTest(img, callback) {
const img = new Image()
img.src = img
img.onload = () => {
callback(true) // should return callback with true on finish
}
img.onerror = (e) => {
callback(false) // should return callback with false on error
console.log(e)
}
}
funcToTest()
测试环境:
describe('tet it', () => {
it('test', done => {
const callback = status => {
expect(status).toEqual(true) // but nothing happen
done()
}
funcToTest('some_image', callback)
})
})
另外我在完成时遇到错误:
Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.
> 2 | it('test', done => {...
提前致谢!
虽然回答了这个问题,但我不同意解决方案的目的。
我们不应该编写测试来通过覆盖率,我们应该编写测试来证明一个观点,并确保代码片段按预期运行。
测试它的方法是模拟 Image 构造函数并将其替换为将调用onload
函数的内容。
describe('tet it', () => {
it('test', done => {
global.Image = class {
constructor() {
setTimeout(() => {
this.onload(); // simulate success
}, 100);
}
}
const callback = status => {
done()
}
funcToTest('some_image', callback)
})
})
该方法只是假设“浏览器”将在 100 毫秒内下载图像,如果您需要在测试之间共享此行为,您可以调整失败的代码,或者将部分代码移动到beforeEach
。
如果您在Image的onload
事件处理程序中有一些逻辑并想测试它是否被正确应用,您实际上可以使用Canvas 以编程方式创建一个图像blob 。
考虑这个函数:
const imageDimensions = (
file: File
): Promise<{ width: number; height: number }> =>
new Promise((resolve, reject) => {
const img = new Image()
img.onload = () => {
const { naturalWidth: width, naturalHeight: height } = img
resolve({ width, height })
}
img.onerror = () => {
reject('There is something wrong with this image')
}
img.src = URL.createObjectURL(file)
})`
因此,给定一个文件(blob),它会返回一个解析为具有维度的对象的Promise 。 或因错误而拒绝。
我们如何测试onload
部分? 您可以在测试中以编程方式创建 blob,但new Blob(['a'.repeat(10)], { type: 'image/jpeg' })
不会触发onload
,因为这不是真正的图像。
不需要模拟任何东西,使用 JSDOM 或类似的东西。 帆布来拯救! 这可以像这样简单地测试:
describe('imageDimensions', () => {
it('should resolve with correct dimensions given an IMAGE blob', done => {
// although this canvas has no actual graphics, it is still an image and it contains image metadata, thus onload() will be fired
const canvas = document.createElement('canvas')
canvas.width = 10
canvas.height = 10
canvas.toBlob(
async blob => {
const { width, height } = await component.imageDimensions(blob)
expect(width).toBe(10)
expect(height).toBe(10)
done()
},
'image/jpeg',
0.1
)
})
it('should reject with an error if the file provided does NOT seem to be an image', async () => {
const file = new Blob(['a'.repeat(10)], { type: 'application/pdf' })
try {
await component.imageDimensions(file)
} catch (error) {
expect(error).toBe('There is something wrong with this image')
}
})
})
在这个例子中,Canvas 正在被创建,然后被转换为一个 blob,这类似于用户选择某个图像文件时由浏览器生成的 blob。
PS 这是 Jest 和 Jasmine 兼容的。
要测试的功能:
index.ts
:
function funcToTest(imgUrl: string, callback: Function) {
const img = new Image();
img.src = imgUrl;
img.onload = () => {
callback(true);
};
img.onerror = e => {
callback(false);
console.log(e);
};
return img;
}
export { funcToTest };
/**
* @jest-environment jsdom
*/
import { funcToTest } from './';
describe('test suites', () => {
it('onload', done => {
const callback = jest.fn(status => {
expect(status).toBe(true);
done();
});
const imageUrl = 'https://github.com/mrdulin';
const img = funcToTest(imageUrl, callback);
if (img.onload) {
const event: any = {};
img.onload(event);
}
});
it('onerror', done => {
const consoleLogSpyOn = jest.spyOn(console, 'log');
const callback = jest.fn(status => {
expect(status).toBe(false);
done();
});
const imageUrl = 'https://github.com/mrdulin';
const img = funcToTest(imageUrl, callback);
if (img.onerror) {
const event: any = { message: 'some error' };
img.onerror(event);
expect(consoleLogSpyOn).toBeCalledWith(event);
}
consoleLogSpyOn.mockRestore();
});
});
单元测试结果和覆盖率:
PASS src/stackoverflow/57092154/index.spec.ts
test suites
✓ onload (8ms)
✓ onerror (8ms)
console.log node_modules/jest-mock/build/index.js:860
{ message: 'some error' }
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 3.821s
这是完整的演示: https : //github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/57092154
更新:未删除的答案,单元测试解决方案 2
您可以使用Object.defineProperty()方法为Image.prototype.onload
方法创建 getter 和 setter。 然后,您可以在测试用例中获取onload
函数并手动执行它。
index.test.ts
:
import { funcToTest } from './';
describe('57092154', () => {
let onloadRef: Function | undefined;
let onerrorRef: Function | undefined;
beforeAll(() => {
Object.defineProperty(Image.prototype, 'onload', {
get() {
return this._onload;
},
set(onload: Function) {
onloadRef = onload;
this._onload = onload;
},
});
Object.defineProperty(Image.prototype, 'onerror', {
get() {
return this._onerror;
},
set(onerror: Function) {
onerrorRef = onerror;
this._onerror = onerror;
},
});
});
it('should handle onload event', () => {
const callback = jest.fn();
funcToTest('./test.png', callback);
onloadRef!();
expect(callback).toBeCalledWith(true);
});
it('should handle onerror event', () => {
const callback = jest.fn();
const logSpy = jest.spyOn(console, 'log').mockImplementation(() => 'suppress error');
funcToTest('./test.png', callback);
const mErr = new Error('network');
onerrorRef!(mErr);
expect(callback).toBeCalledWith(false);
expect(logSpy).toBeCalledWith(mErr);
});
});
单元测试结果:
PASS examples/57092154/index.test.ts
57092154
✓ should handle onload event (4 ms)
✓ should handle onerror event
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
index.ts | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 3.741 s
@Patrick 答案的打字稿版本:
type ImageConstructor = new (
width?: number | undefined,
height?: number | undefined
) => HTMLImageElement;
describe('1234', () => {
global.Image = class {
onload: () => void;
constructor() {
this.onload = jest.fn();
setTimeout(() => {
this.onload();
}, 50);
}
} as unknown as ImageConstructor;
it("should call onload", () => {})
)};
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.