使用打字稿,我正在尝试测试动态元素。但对此一无所知。有人帮我吗?
这是我的ts文件:
export default class MyClass {
constructor(){
this.render();
}
render() {
const el:HTMLInputElement = document.createElement('input') as HTMLInputElement;
const link:HTMLAnchorElement = document.createElement('a') as HTMLAnchorElement;
const container:HTMLBodyElement = document.querySelector('body') as HTMLBodyElement;
link.innerHTML = "Click Me!";
link.setAttribute('href', '#');
link.setAttribute('target', '_blank');
el.setAttribute('type', 'file');
container.appendChild(el);
container.appendChild(link);
el.addEventListener('change', (event) => {
if('files' in el) {
const availFile = el.files[0];
const blob = new Blob([availFile], { type: availFile.type});
const objectURL = window.URL.createObjectURL(blob);
link.setAttribute('href', objectURL);
}
})
}
}
new MyClass();
我的test.spec文件:
import MyClass from "./index";
jest.mock('./index');
describe("testing as first", () => {
it("we can test the constructor", () => {
const myClass = new MyClass();
expect(MyClass).toHaveBeenCalledTimes(1);
});
it('will render element', () => {
const myClass = new MyClass();
myClass.render();
expect(document.querySelector('input')).toBeTruthy();
expect(document.querySelector('a')).toBeTruthy();
})
it('will render element', () => {
const myClass = new MyClass();
myClass.render();
})
});
覆盖结果:
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 75 | 0 | 75 | 75 | |
index.ts | 75 | 0 | 75 | 75 | 21,22,23,25,26 |
----------|----------|----------|----------|----------|-------------------|
但是如何测试未覆盖的行?请帮助我。
这是单元测试解决方案:
index.ts
:
export default class MyClass {
constructor() {
this.render();
}
render() {
const el: HTMLInputElement = document.createElement('input') as HTMLInputElement;
const link: HTMLAnchorElement = document.createElement('a') as HTMLAnchorElement;
const container: HTMLBodyElement = document.querySelector('body') as HTMLBodyElement;
link.innerHTML = 'Click Me!';
link.setAttribute('href', '#');
link.setAttribute('target', '_blank');
el.setAttribute('type', 'file');
container.appendChild(el);
container.appendChild(link);
el.addEventListener('change', (event) => {
if ('files' in el) {
const availFile: File = el.files![0];
const blob = new Blob([availFile], { type: availFile.type });
const objectURL = window.URL.createObjectURL(blob);
link.setAttribute('href', objectURL);
}
});
}
}
index.test.ts
:
import MyClass from './index';
describe('testing as first', () => {
let createElement;
let querySelector;
let createObjectURL;
beforeEach(() => {
createElement = document.createElement;
querySelector = document.querySelector;
createObjectURL = window.URL.createObjectURL;
});
afterEach(() => {
jest.restoreAllMocks();
document.createElement = createElement;
document.querySelector = querySelector;
window.URL.createObjectURL = createObjectURL;
});
it('should call render method', () => {
jest.spyOn(MyClass.prototype, 'render').mockReturnValueOnce();
const myClass = new MyClass();
expect(myClass.render).toBeCalledTimes(1);
});
it('should render element and handle change event if input has files', () => {
const myClass = new MyClass();
const mAvailFile = new Blob(['I am file'], { type: 'text/html' });
const mInput = { setAttribute: jest.fn(), addEventListener: jest.fn(), files: [mAvailFile] };
const mLink = { setAttribute: jest.fn(), innerHTML: '' };
const mContainer = { appendChild: jest.fn() };
document.createElement = jest.fn().mockImplementation((tagName) => {
switch (tagName) {
case 'input':
return mInput;
case 'a':
return mLink;
}
});
document.querySelector = jest.fn().mockReturnValueOnce(mContainer);
mInput.addEventListener.mockImplementationOnce((event, callback) => {
callback();
});
const mObjectURL = 'blob:https://www.google.com/6e165b50-979b-43f6-b685-7163413f0faf';
window.URL.createObjectURL = jest.fn().mockReturnValueOnce(mObjectURL);
myClass.render();
expect(document.createElement).toBeCalledTimes(2);
expect(document.querySelector).toBeCalledWith('body');
expect(mLink.innerHTML).toBe('Click Me!');
expect(mLink.setAttribute.mock.calls[0]).toEqual(['href', '#']);
expect(mLink.setAttribute.mock.calls[1]).toEqual(['target', '_blank']);
expect(mInput.setAttribute).toBeCalledWith('type', 'file');
expect(mContainer.appendChild.mock.calls[0]).toEqual([mInput]);
expect(mContainer.appendChild.mock.calls[1]).toEqual([mLink]);
expect(mInput.addEventListener).toBeCalledWith('change', expect.any(Function));
expect(window.URL.createObjectURL).toBeCalledWith(new Blob([mAvailFile], { type: mAvailFile.type }));
expect(mLink.setAttribute.mock.calls[2]).toEqual(['href', mObjectURL]);
});
it('should render element', () => {
const myClass = new MyClass();
const mInput = { setAttribute: jest.fn(), addEventListener: jest.fn() };
const mLink = { setAttribute: jest.fn(), innerHTML: '' };
const mContainer = { appendChild: jest.fn() };
document.createElement = jest.fn().mockImplementation((tagName) => {
switch (tagName) {
case 'input':
return mInput;
case 'a':
return mLink;
}
});
document.querySelector = jest.fn().mockReturnValueOnce(mContainer);
mInput.addEventListener.mockImplementationOnce((event, callback) => {
callback();
});
myClass.render();
expect(document.createElement).toBeCalledTimes(2);
expect(document.querySelector).toBeCalledWith('body');
expect(mLink.innerHTML).toBe('Click Me!');
expect(mLink.setAttribute.mock.calls[0]).toEqual(['href', '#']);
expect(mLink.setAttribute.mock.calls[1]).toEqual(['target', '_blank']);
expect(mInput.setAttribute).toBeCalledWith('type', 'file');
expect(mContainer.appendChild.mock.calls[0]).toEqual([mInput]);
expect(mContainer.appendChild.mock.calls[1]).toEqual([mLink]);
expect(mInput.addEventListener).toBeCalledWith('change', expect.any(Function));
});
});
单元测试结果覆盖率100%:
PASS src/stackoverflow/59833555/index.test.ts (14.002s)
testing as first
✓ should call render method (6ms)
✓ should render element and handle change event if input has files (19ms)
✓ should render element (13ms)
----------|----------|----------|----------|----------|-------------------|
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: 3 passed, 3 total
Snapshots: 0 total
Time: 16.304s, estimated 17s
源代码:https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59833555