开玩笑-如何重组代码以使其可测试?

时间:2019-10-31 21:10:21

标签: unit-testing jestjs puppeteer

我要添加两个单元测试,以涵盖选择器是否在页面上的情况。

async function getPrice(page, url) {
    const priceSelector = '#price';
    if (await page.$(priceSelector)) {
        return page.$eval(priceSelector, elem => elem.innerText);
    }
    return null;
}

page在另一个函数中定义:

const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(url);

我尝试模拟page,以使page.$(priceSelector)返回真实或错误,但没有成功。 doc中的模拟模块示例很有意义,但实际上我的代码是否可以测试?如果没有,应该如何构造?

1 个答案:

答案 0 :(得分:1)

只需重构一个位置,最好将回调函数elem => elem.innerText提取到一个新函数中。

例如

index.ts

export async function getPrice(page, url) {
  const priceSelector = '#price';
  if (await page.$(priceSelector)) {
    return page.$eval(priceSelector, elem => elem.innerText);
  }
  return null;
}

index.spec.ts

import { getPrice } from './';

const page = {
  $: jest.fn(),
  $eval: jest.fn()
};

beforeEach(() => {
  jest.resetAllMocks();
});

test('should eval', async () => {
  page.$.mockResolvedValueOnce(true);
  page.$eval.mockReturnValueOnce('dummy data');
  const actualValue = await getPrice(page, 'example.com');
  expect(actualValue).toBe('dummy data');
  expect(page.$).toBeCalledWith('#price');
  expect(page.$eval).toBeCalledWith('#price', expect.any(Function));
});

test('should return null', async () => {
  page.$.mockResolvedValueOnce(false);
  const actualValue = await getPrice(page, 'example.com');
  expect(actualValue).toBeNull();
  expect(page.$).toBeCalledWith('#price');
  expect(page.$eval).not.toBeCalled();
});

您可以像这样测试它,但是回调函数将不会被测试和覆盖。

带有覆盖率报告的单元测试结果:

 PASS  src/stackoverflow/58651192/index.spec.ts
  ✓ should eval (6ms)
  ✓ should return null (2ms)

----------|----------|----------|----------|----------|-------------------|
File      |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files |    85.71 |      100 |       50 |      100 |                   |
 index.ts |    85.71 |      100 |       50 |      100 |                   |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        4.786s, estimated 7s

如果我们像这样提取$eval的回调函数:

export const evalCallback = elem => elem.innerText;

我们可以轻松对其进行测试:

test('evalCallback', () => {
  const actualValue = evalCallback({ innerText: 'unit test' });
  expect(actualValue).toBe('unit test');
});

覆盖率100%的单元测试结果:

 PASS  src/stackoverflow/58651192/index.spec.ts (9.066s)
  ✓ should eval (10ms)
  ✓ should return null (1ms)
  ✓ evalCallback (1ms)

----------|----------|----------|----------|----------|-------------------|
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:        10.804s