typescript 如何为Jest测试模拟ServiceBusError异常?

uklbhaso  于 2023-01-18  发布在  TypeScript
关注(0)|答案(2)|浏览(189)

我有一个类似的azure函数(不是实际的函数,只是下面给出的一个表示)

import { AzureFunction, Context, HttpRequest } from '@azure/functions'
import { ServiceBusClient, isServiceBusError } from '@azure/service-bus'

const serviceBusClient = new ServiceBusClient(clientConnectionString)
const sender = serviceBusClient.createSender(topicName)

const func: AzureFunction = async function (
  context: Context,
  req: HttpRequest
): Promise<void> { 
try {
sender.sendMessages({ body: {a:"a",b:"b",c:"c" })
}
catch (error: unknown) {
if (isServiceBusError(error)) {
      message = `This is the error message ${error.message} with the reason ${error.code}`
      context.log.error(`Error Message: ${message}`)
    } else {
      context.log.error(`Error Message: Error Encountered`)
    }
}
}

如何测试catch块中的代码是否正常工作?具体来说,如何抛出一个ServiceBusError来测试它是否被捕获并记录相应的错误消息?
是否有人能够就如何开展工作提供指导或提示?

    • 更新**

我已经有了下面的代码来模拟服务总线&同时检查错误是否为Service bus error。但是,我不明白如何让这个函数同时模拟"好"的情况(消息发送成功)和"坏"的情况(抛出ServiceBusError)。

jest.mock('@azure/service-bus', () => {
  return {
    isServiceBusError: jest.fn().mockImplementation((err: unknown) => {
      if (err === ServiceBusError) {
        return true
      } else return false
    }),
    ServiceBusClient: jest.fn().mockImplementation(() => {
      return {
        createSender: jest.fn().mockImplementation(() => {
          return {
            sendMessages: mockSendMessages,
          }
        }),
      }
    }),
  }
})

我测试是这样的

test('Bad case: Empty Data', async () => {
  const emptyData = {}

  const wrongRequest = {
    query: {},
    body: emptyData,
  }

  // Action
  await func(context, wrongRequest)

  expect(mockSendMessages).toHaveBeenCalledTimes(0)
})

如何使用mock抛出错误是我感到困惑的地方(仅供参考,上面的测试用例工作得很好)。

6yt4nkrj

6yt4nkrj1#

使用模拟测试

您需要将sender.sendMessages函数替换为您可以控制的模拟版本,例如在测试用例中抛出异常。
使用你可以在测试代码中控制的服务、对象或函数的模拟版本来模拟任意条件、状态或错误的技术是一个成熟的最佳实践。你可以在互联网上找到大量关于 * 模拟测试 * 的信息。
有关模拟的一般信息:

使用Jest Manual Mocks创建模拟服务总线客户端

实际上,您必须使用Jest手动模拟,以便

  1. ServiceBusClient导入将导入模拟版本,而不是真实的版本
    1.模拟版本的createSender方法返回一个模拟发送者。
    1.模拟发送者的sendMessages方法可以在测试中做任何你想让它做的事情(例如抛出一个特定的异常),你可以在测试调用被测代码之前配置模拟来完成。
    以下是一些可以帮助您的Jest和Typescript特定资源:
gopyfrb3

gopyfrb32#

下面是一个符合您需求的Jest单元测试源代码示例(我希望如此):

import type { AzureFunction, Context } from '@azure/functions'
import type { ServiceBusSender } from '@azure/service-bus'
import { when } from 'jest-when'

describe('service bus client unit tests', () => {
  const clientConnectionString = 'myConnectionString'
  const topicName = 'myTopic'
  let ServiceBusClient: jest.SpyInstance
  let isServiceBusError: jest.SpyInstance
  let serviceBusClient: { createSender: (topicName: string) => ServiceBusSender }
  let sender: jest.Mocked<ServiceBusSender>
  let func: AzureFunction
  beforeAll(() => {
    ServiceBusClient = jest.fn()
    isServiceBusError = jest.fn()
    serviceBusClient = { createSender: jest.fn() }
    sender = { sendMessages: jest.fn() } as unknown as jest.Mocked<ServiceBusSender>
    jest.doMock('@azure/service-bus', () => ({ ServiceBusClient, isServiceBusError }))
    when(ServiceBusClient).calledWith(clientConnectionString).mockReturnValue(serviceBusClient)
    when(serviceBusClient.createSender).calledWith(topicName).mockReturnValue(sender)
    func = require('./sample').func
    expect(ServiceBusClient).toHaveBeenNthCalledWith(1, clientConnectionString)
    expect(serviceBusClient.createSender).toHaveBeenNthCalledWith(1, topicName)
  })
  afterEach(() => {
    jest.resetAllMocks()
  })
  afterAll(() => {
    jest.restoreAllMocks()
  })
  test('should log error given a service bus error', async () => {
    // Given
    const contextLogError = jest.fn()
    const context = { log: { error: contextLogError } } as unknown as Context
    const messageBody = { body: { a: 'a', b: 'b', c: 'c' } }
    const error: Error & { code?: string } = new Error('oops')
    error.code = 'MyCode'
    when(sender.sendMessages).calledWith(messageBody).mockRejectedValue(error)
    when(isServiceBusError).calledWith(error).mockReturnValue(true)
    // When
    await func(context)
    // Then
    expect(sender.sendMessages).toHaveBeenNthCalledWith(1, messageBody)
    expect(isServiceBusError).toHaveBeenNthCalledWith(1, error)
    expect(contextLogError).toHaveBeenNthCalledWith(
      1,
      'Error Message: This is the error message oops with the reason MyCode',
    )
  })
  test('should log error given any other error', async () => {
    // Given
    const contextLogError = jest.fn()
    const context = { log: { error: contextLogError } } as unknown as Context
    const messageBody = { body: { a: 'a', b: 'b', c: 'c' } }
    const error = new Error('oops')
    when(sender.sendMessages).calledWith(messageBody).mockRejectedValue(error)
    when(isServiceBusError).calledWith(error).mockReturnValue(false)
    // When
    await func(context)
    // Then
    expect(sender.sendMessages).toHaveBeenNthCalledWith(1, messageBody)
    expect(isServiceBusError).toHaveBeenNthCalledWith(1, error)
    expect(contextLogError).toHaveBeenNthCalledWith(1, 'Error Message: Error Encountered')
  })
})

相关问题