微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何设置此JS代码以进行更好的测试?

如何解决如何设置此JS代码以进行更好的测试?

大家好,我在使用Jest测试下面的JS时遇到问题。它以waitForWorker开头。如果响应是“正在工作”,则它将再次调用waitForWorker()。我尝试了Jest测试,但是我不知道如何测试内部函数调用,而且我一直在研究并失败。

const $ = require('jquery')
const axios = require('axios')

let workerComplete = () => {
  window.location.reload()
}

async function checkWorkerStatus() {
  const worker_id = $(".worker-waiter").data('worker-id')
  const response = await axios.get(`/v1/workers/${worker_id}`)
  return response.data
}

function waitForWorker() {
  if (!$('.worker-waiter').length) {
    return
  }

  checkWorkerStatus().then(data => {
      // delay next action by 1 second e.g. calling api again
      return new Promise(resolve => setTimeout(() => resolve(data),1000));
    }).then(worker_response => {
    const working_statuses = ['queued','working']
    if (worker_response && working_statuses.includes(worker_response.status)) {
      waitForWorker()
    } else {
      workerComplete()
    }
  })
}

export {
  waitForWorker,checkWorkerStatus,workerComplete
}

if (process.env.NODE_ENV !== 'test') $(waitForWorker)

由于无法与任何人再次核对,因此我的部分测试如下。我不知道在测试中是否两次调用await Worker.checkWorkerStatus()是最好的方法,因为如果响应data.status正在“工作”,则waitForWorker应该再次调用它。

import axios from 'axios'
import * as Worker from 'worker_waiter'

jest.mock('axios')

beforeAll(() => {
  Object.defineProperty(window,'location',{
    value: { reload: jest.fn() }
  })
});

beforeEach(() => jest.resetAllMocks() )

afterEach(() => {
  jest.restoreAllMocks();
});

describe('worker is complete after 2 API calls a',() => {
  const worker_id = Math.random().toString(36).slice(-5) // random string

  beforeEach(() => {
      axios.get
      .mockResolvedValueOnce({ data: { status: 'working' } })
      .mockResolvedValueOnce({ data: { status: 'complete' } })
      jest.spyOn(Worker,'waitForWorker')
      jest.spyOn(Worker,'checkWorkerStatus')
    document.body.innerHTML = `<div class="worker-waiter" data-worker-id="${worker_id}"></div>`
  })

  it('polls the correct endpoint twice a',async() => {
    const endpoint = `/v1/workers/${worker_id}`

    await Worker.checkWorkerStatus().then((data) => {
      expect(axios.get.mock.calls).toMatchObject([[endpoint]])
      expect(data).toMatchObject({"status": "working"})
    })
    await Worker.checkWorkerStatus().then((data) => {
      expect(axios.get.mock.calls).toMatchObject([[endpoint],[endpoint]])
      expect(data).toMatchObject({"status": "complete"})
    })
  })


  it('polls the correct endpoint twice b',async() => {
    jest.mock('waitForWorker',() => {
      expect(Worker.checkWorkerStatus).toBeCalled()
    })
    expect(Worker.waitForWorker).toHaveBeenCalledTimes(2)
    await Worker.waitForWorker()
  })

解决方法

我认为您可以在这里做几件事。

注入状态处理程序

您可以通过将waitForWorker依赖性和副作用注入函数中来使其更加明确,这可以让您完全对被测系统进行黑盒测试并断言触发了正确的注入效应。这就是所谓的依赖注入。

function waitForWorker(onComplete,onBusy) {
   // instead of calling waitForWorker call onBusy.
   // instead of calling workerComplete call onComplete.
}

现在要测试,您真的只需要创建模拟函数即可。

const onComplete = jest.fn();
const onBusy = jest.fn();

并断言那些被按您期望的方式调用。此功能也是异步的,因此您需要确保您的笑话测试知道完成情况。我注意到您在测试中使用async,但是您当前的函数不会返回待处理的Promise,因此测试将同步完成。

兑现诺言

您可以返回承诺并测试其竞争力。现在,您所拥有的承诺不会暴露在waitForWorker之外。

async function waitForWorker() {
  let result = { status: 'empty' };

  if (!$('.worker-waiter').length) {
    return result;
  }
  
  try {
    const working_statuses = ['queued','working'];
    const data = await checkWorkerStatus();

    if (data && working_statuses.includes(data.status)) {
      await waitForWorker();
    } else {
      result = { status: 'complete' };
    }
  } catch (e) {
    result = { status: 'error' };
  }

  return result;
}

以上示例将您的函数转换为async以提高可读性并消除了副作用。我返回了一个带有状态的异步结果,这很有用,因为waitForWorker可以完成许多分支。这将告诉您,在axios设置下,承诺最终会以某种状态完成。然后,您可以使用覆盖率报告来确保您关心的分支已执行,而不必担心测试内部实现细节。

如果您确实想测试内部实现细节,则可能需要合并我上面提到的一些注入原理。

async function waitForWorker(request) {
 // ...

  try {
    const working_statuses = ['queued','working'];
    const data = await request();
  } catch (e) {
    // ...
  }
  
  // ...
}

然后,您可以将任何函数(甚至是模拟)注入此函数中,并确保以所需的方式调用它,而不必模拟axios。在您的应用程序中,您只需注入checkWorkerStatus

  const result = await waitForWorker(checkWorkerStatus);
  
  if (result.status === 'complete') {
    workerComplete();
  }

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?