使用异步函数模拟请求错误
Simulating request error with async function
在我的 express
应用程序中,我这样声明请求处理程序(此处已简化):
export const CreateProduct = async (req, res, next) => {
try {
// ProductModel is a sequelize defined model
const product = await ProductModel.create(req.body)
res.send(product)
} catch (err) {
// I have raygun setup as the error handler for express
// in this example, it will finally return 500
next(err)
}
}
然后像这样使用它:
import { CreateProduct } from './create_product'
export const ProductRouter = express.Router()
ProductRouter.post('/', CreateProduct)
然而,当 运行 我的测试时,nyc
/istanbul
会抱怨行 9
是一个 Uncovered Line
(在我的例子中,它是next(err)
函数),我该如何模拟示例中的错误?
更容易的方法是为您的 ProductModel
创建一个验证机制,当您使用无效数据创建产品时会抛出一些验证错误。
并且在您的 mocha 中,您将发送一个无效的产品主体,它会捕获您的 next
。
执行此操作的两种最简单的方法如下:
1) 承认控制器无法进行单元测试,并且因为它们无法进行单元测试,所以它们将成为您集成测试的一部分,因为这是事实,您将尽可能多地进行单元测试尽可能从它们中编写代码,并将它们限制在编写能够测试的代码段之外。这样就好了;无论如何,从单个请求的角度来看,Express 控制器有点像应用程序中的 main
方法,因此只要 main
没有做繁重的工作,而是实例化和 运行 正在做的事情。
2) 编写你的函数,使它们都接收到它们完成工作所需的所有道具,而且,它们几乎总是从其他文件(而不是它们在其中定义的文件)中接收到它们,除非分配只是作为默认值。
在控制器的情况下,如果您希望控制器是可单元测试的,您可以将其预先配置为接受 ProductModel
使用(或使用的功能),而不是假设您要去拉入真实模型。
export const CreateProduct = (ProductModel) => async (req, res, next) => {
const productData = req.body
try {
const product = await ProductModel.create(productData)
res.send(product)
} catch (err) {
next(err)
}
}
import { CreateProduct } from './create_product'
import { ConfigureProductModel } from './somewhere_else'
export const ProductRouter = express.Router()
ProductRouter.post('/', CreateProduct(ConfigureProductModel(database)))
现在要测试它,您可以轻松地创建一个 CreateProduct
,并在其中传入一个假的 ProductModel
。如果你使用这个例子,ConfigureProductModel
作为一个工厂,你可以通过给它传递一个假的数据库实例来测试 it(如果你这样做,确实有那个级别的控制).
就我个人而言,就像我说的,作为一个控制器,我想尽可能多地移除控制,所以我可能会去掉大部分命令式代码。
const CreateProduct = ProductModel => (req, res, next) =>
ProductModel.create(req.body)
.then(product => res.send(product))
.catch(next);
在我的 express
应用程序中,我这样声明请求处理程序(此处已简化):
export const CreateProduct = async (req, res, next) => {
try {
// ProductModel is a sequelize defined model
const product = await ProductModel.create(req.body)
res.send(product)
} catch (err) {
// I have raygun setup as the error handler for express
// in this example, it will finally return 500
next(err)
}
}
然后像这样使用它:
import { CreateProduct } from './create_product'
export const ProductRouter = express.Router()
ProductRouter.post('/', CreateProduct)
然而,当 运行 我的测试时,nyc
/istanbul
会抱怨行 9
是一个 Uncovered Line
(在我的例子中,它是next(err)
函数),我该如何模拟示例中的错误?
更容易的方法是为您的 ProductModel
创建一个验证机制,当您使用无效数据创建产品时会抛出一些验证错误。
并且在您的 mocha 中,您将发送一个无效的产品主体,它会捕获您的 next
。
执行此操作的两种最简单的方法如下:
1) 承认控制器无法进行单元测试,并且因为它们无法进行单元测试,所以它们将成为您集成测试的一部分,因为这是事实,您将尽可能多地进行单元测试尽可能从它们中编写代码,并将它们限制在编写能够测试的代码段之外。这样就好了;无论如何,从单个请求的角度来看,Express 控制器有点像应用程序中的 main
方法,因此只要 main
没有做繁重的工作,而是实例化和 运行 正在做的事情。
2) 编写你的函数,使它们都接收到它们完成工作所需的所有道具,而且,它们几乎总是从其他文件(而不是它们在其中定义的文件)中接收到它们,除非分配只是作为默认值。
在控制器的情况下,如果您希望控制器是可单元测试的,您可以将其预先配置为接受 ProductModel
使用(或使用的功能),而不是假设您要去拉入真实模型。
export const CreateProduct = (ProductModel) => async (req, res, next) => {
const productData = req.body
try {
const product = await ProductModel.create(productData)
res.send(product)
} catch (err) {
next(err)
}
}
import { CreateProduct } from './create_product'
import { ConfigureProductModel } from './somewhere_else'
export const ProductRouter = express.Router()
ProductRouter.post('/', CreateProduct(ConfigureProductModel(database)))
现在要测试它,您可以轻松地创建一个 CreateProduct
,并在其中传入一个假的 ProductModel
。如果你使用这个例子,ConfigureProductModel
作为一个工厂,你可以通过给它传递一个假的数据库实例来测试 it(如果你这样做,确实有那个级别的控制).
就我个人而言,就像我说的,作为一个控制器,我想尽可能多地移除控制,所以我可能会去掉大部分命令式代码。
const CreateProduct = ProductModel => (req, res, next) =>
ProductModel.create(req.body)
.then(product => res.send(product))
.catch(next);