网站搜索

javascript - 关于嵌套 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 块的测试覆盖率,但我似乎无法让它到达。我不确定应在何时强制抛出异常,然后如何传递并进行测试。有人有关于我应该如何处理或我漏掉了什么的建议吗?

答案#1

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

return client(getServiceUrl(path), 'PUT', getServiceHeaders(token), body)

你在这一行周围使用的 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)
  }
}

或者,你可以使用常规的 promise catch:

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

(注意:我没有完全查看代码——里面可能还有更多存在异步问题的地方)