首页 > 解决方案 > 关于嵌套 try catch 语句和错误处理的问题

问题描述

由于不同的包装器和管道中相互传递的东西,我正在处理多个嵌套的 try catch 语句。

我试图通过模拟响应并在数据最终传递到某个地方调用 .json() 并抛出如下定义的错误时抛出一个名为 put 的函数的 catch 块的测试覆盖率:

it('hits the catch', async () => {
        fetch.mockImplementation(() =>
          Promise.resolve({
            ok: true,
            json: () => {
              throw { type: 'error', message: 'hit the catch' }
            },
          })
        )
    
        expect(
          await put({
            path: '/script/1',
            body: {
              name: 'newName',
            },
          })
        ).toThrow({
          message: 'hit the catch',
          type: 'error',
        })
      })

该函数依次调用客户端函数:

export const put = async <I, R>({ path, body }: { path: string; body: I }) => {
  try {
    return client<I>(getServiceUrl(path), 'PUT', getServiceHeaders(token), body)
  } catch (e) {
    throw handleClientError(e)
    // return handleClientError(e)
  }
}

客户端函数是 fetch 的包装器:

    export const client = async <I>(
      url: string,
      method: 'POST' | 'GET' | 'PUT' | 'PATCH' | 'DELETE',
      headers: HeadersInit,
      body?: I
    ): Promise<ServiceResonse<I>> => {
      try {
        const res: Response = await fetch(url, {
          method,
          headers,
          body: JSON.stringify(body),
        })
    
        return getServiceResponse(res)
      } catch (e) {
        throw handleClientError(e)
        // return handleClientError(e)
      }
}

这里也调用了各种辅助函数:

export const getServiceHeaders = (token: string = ''): HeadersInit => {
  try {
    if (token) {
      return {
        'Content-Type': 'application/json',
        Authorization: 'Bearer ' + token,
      }
    } else {
      return {
        'Content-Type': 'application/json',
      }
    }
  } catch (e) {
    const msg = 'Error getting service headers ' + JSON.stringify(e)
    console.error(msg) //shouldn't ever happen
    throw new Error(msg)
  }
}

export const getServiceResponse = async <T>(
  response: Response
): Promise<ServiceResonse<T>> => {
  try {
    const data = await response.json()
    if (!response.ok) {
      let error = {
        code: (data && data.code) || 'error',
        message:
          (data && data.message) ||
          response.statusText ||
          'Unkown Error x03391aa00',
        invalid: null,
      }

      switch (response.status) {
        case 400:
          if (error.code == 'invalid') {
            return (error.invalid = ((data && data.errors) || []).reduce(
              (items: RequestErrorResponseFields, i: APIResponseErrorItem) => {
                items[i.field] = i.message
                return items
              },
              {}
            ))
          }

          break
        case 401:
          // TODO handle error)
          return { type: 'error', fields: {}, message: 'Invalid Token' }
          break
      }
      return Promise.reject(error)
    } else {
      return data
    }
  } catch (e) {
    throw e
  }
}

export const getServiceUrl = (
  path: string,
  options: { [k: string]: string | number } = {}
): string => {
  if (typeof path !== 'string') throw new Error('path must be of type string')
  if (typeof options !== 'object')
    throw new Error('options must be of type object')

  const query = Object.keys(options)
    .map(k => k + '=' + options[k])
    .join('&')

  //todo move to general configuration file, still use process.env
  const baseUrl = process.env.API_URL || 'http://localhost/ipa'
  return `${baseUrl}${path}${query.length ? '?' + query : ''}`
}

export const handleClientError = (
  err: { message?: string } | RequestErrorResponse
) => {
  if ('type' in err) {
    throw err
    // return Promise.reject(err)
  } else {
    throw {
      code: 'error',
      message: err.message || 'Unkown - 0x39aef3991',
      errors: [],
    }
    // return Promise.reject({
    //   code: 'error',
    //   message: err.message || 'Unkown - 0x39aef3991',
    //   errors: [],
    // })
  }
}

我试图通过强制异常并将其传递给 catch 来测试 put 函数中的 catch 块的覆盖率,但我似乎无法达到它。我不确定在什么时候强制和异常,然后如何传递它并对其进行测试。有没有人对我应该如何处理这个或我缺少什么有任何想法?

标签: javascriptreactjstypescriptrestunit-testing

解决方案


在你的async put函数中,你有一行返回一个 Promise:

返回客户端(getServiceUrl(路径),'PUT',getServiceHeaders(令牌),正文)

这条线周围的 try/catch 块将捕获同步抛出的错误,但不会捕获异步错误。如果要使用try/catch块来捕获这些异步错误,则需要添加一条await语句:

export const put = async <I, R>({ path, body }: { path: string; body: I }) => {
  try {
    return await client<I>(getServiceUrl(path), 'PUT', getServiceHeaders(token), body)
  } catch (e) {
    throw handleClientError(e)
    // return handleClientError(e)
  }
}

或者,您可以使用常规的承诺捕获:

return await client<I>(getServiceUrl(path), 'PUT', getServiceHeaders(token), body).catch(handleClientError)

(注意:我没有完全浏览代码——那里可能有更多的异步问题)


推荐阅读