Express/Typescript 使用 Jest/Supertest 进行测试
Express/Typescript testing with Jest/Supertest
我目前正在尝试测试 express API,我正在使用 Jest 和 Supertest,但我似乎无法让它工作。
我的代码是:
router.get('/', async (req: Request, res: Response) => {
const products: ProductType[] = await ProductModel.find({});
res.send(products);
});
我的测试是:
describe('GET /', () => {
it('calls ProductModel.find and returns products', async () => {
const mockproducts = 'this is a product';
ProductModel.find = jest.fn().mockResolvedValueOnce(mockproducts);
const response = await request(products).get('/');
expect(response).toBe(mockproducts);
});
});
所以基本上,模拟的解析值都工作正常但是当我 运行 测试时,res.send 不工作。
TypeError: res.send is not a function
谁能告诉我这是什么问题?
谢谢!
Could anyone advise what the problem is here?
您在可以避免的单元测试中使用 supertest
。 supertest
也接受了您的 express 应用程序的实例,并且似乎提供了 products
?还是 products
您的快递实例?您可能会发现的另一个问题是 ProductModel.find
直到 在 测试调用之后才被模拟,因为您正在使用全局实例。
在测试时,通过在设计代码时考虑到清晰的抽象和测试,我们可以让我们的生活变得更轻松。
依赖项
当您设计代码时,将代码设计为接受依赖实例 arguments/properties:
// as an argument
function makeHttpRequest(path, httpClient: AxoisInstance) {
return httpClient.get(path);
}
// as a property of object/class
class DependsOn {
constructor(private readonly httpClient: AxoisInstance) {}
request(path: string) {
return this.httpClient.get(path);
}
}
</pre>
这使我们的测试更容易,因为我们可以自信地说正确的实例(真实或模拟)已提供给控制器、服务、存储库等。
这也避免了使用像这样的东西:
// ... some bootstrap function
if (process.env.NODE_ENV === 'test') {
someInstance = getMockInstance()
} else {
someInstance = RealInstance();
}
</pre>
单独关注
当您处理请求时,需要做一些事情:
- 路由(映射路由处理程序)
- 控制器(你的路由处理器)
- 服务(与 Repositories/Models/Entities 交互)
- 模型(您的
ProductModel
或数据层)
你目前拥有所有这些内联(我认为我们 99.99% 的人在选择 Express 时都会这样做)。
// product.routes.ts
router.get('/', ProductController.get); // pass initialised controller method
// product.controller.ts
class ProductController {
constructor(private readonly service: ProductService) {}
get(request: Request, response: Response) {
// do anything with request, response (if needed)
// if you need validation, try middleware
response.send(await this.service.getAllProducts());
}
}
// product.service.ts
class ProductService {
// Model IProduct (gets stripped on SO)
constructor(private readonly model: Model) {}
getAllProducts() {
return this.model.find({});
}
}
</pre>
测试
我们现在剩下几个可以轻松测试的组件,以确保正确的输入产生正确的输出。在我看来,jest 是最简单的模拟方法的工具之一,类,以及其他所有提供良好抽象的工具。
// product.controller.test.ts
it('should call service.getAllProducts and return response', async () => {
const products = [];
const response = {
send: jest.fn().mockResolvedValue(products),
};
const mockModel = {
find: jest.fn().mockResolvedValue(products),
};
const service = new ProductService(mockModel);
const controller = new ProductController(service);
const undef = await controller.get({}, response);
expect(undef).toBeUndefined();
expect(response.send).toHaveBeenCalled();
expect(response.send).toHaveBeenCalledWith(products);
expect(mockModel.find).toHaveBeenCalled();
expect(mockModel.find).toHaveBeenCalledWith();
});
// product.service.test.ts
it('should call model.find and return response', async () => {
const products = [];
const mockModel = {
find: jest.fn().mockResolvedValue(products),
};
const service = new ProductService(mockModel);
const response = await service.getAllProducts();
expect(response).toStrictEqual(products);
expect(mockModel.find).toHaveBeenCalled();
expect(mockModel.find).toHaveBeenCalledWith();
});
// integration/e2e test (app.e2e-test.ts) - doesn't run with unit tests
// test everything together (mocking should be avoided here)
it('should return the correct response', () => {
return request(app).get('/').expect(200).expect(({body}) => {
expect(body).toStrictEqual('your list of products')
});
})
</pre>
对于您的应用程序,您需要确定将依赖项注入正确 类 的合适方法。您可能会决定接受所需模型的 main
函数适合您,或者可能会认为 https://www.npmjs.com/package/injection-js 等更强大的功能会起作用。
避免 OOP
如果您希望避免使用对象,请接受实例作为函数参数:productServiceGetAll(params: SomeParams, model?: ProductModel)
.
了解更多
- https://www.guru99.com/unit-testing-guide.html
- https://jestjs.io/docs/mock-functions
- https://levelup.gitconnected.com/typescript-object-oriented-concepts-in-a-nutshell-cb2fdeeffe6e?gi=81697f76e257
- https://www.npmjs.com/package/supertest
- https://tomanagle.medium.com/strongly-typed-models-with-mongoose-and-typescript-7bc2f7197722
我目前正在尝试测试 express API,我正在使用 Jest 和 Supertest,但我似乎无法让它工作。
我的代码是:
router.get('/', async (req: Request, res: Response) => {
const products: ProductType[] = await ProductModel.find({});
res.send(products);
});
我的测试是:
describe('GET /', () => {
it('calls ProductModel.find and returns products', async () => {
const mockproducts = 'this is a product';
ProductModel.find = jest.fn().mockResolvedValueOnce(mockproducts);
const response = await request(products).get('/');
expect(response).toBe(mockproducts);
});
});
所以基本上,模拟的解析值都工作正常但是当我 运行 测试时,res.send 不工作。
TypeError: res.send is not a function
谁能告诉我这是什么问题?
谢谢!
Could anyone advise what the problem is here?
您在可以避免的单元测试中使用 supertest
。 supertest
也接受了您的 express 应用程序的实例,并且似乎提供了 products
?还是 products
您的快递实例?您可能会发现的另一个问题是 ProductModel.find
直到 在 测试调用之后才被模拟,因为您正在使用全局实例。
在测试时,通过在设计代码时考虑到清晰的抽象和测试,我们可以让我们的生活变得更轻松。
依赖项
当您设计代码时,将代码设计为接受依赖实例 arguments/properties:
// as an argument function makeHttpRequest(path, httpClient: AxoisInstance) { return httpClient.get(path); } // as a property of object/class class DependsOn { constructor(private readonly httpClient: AxoisInstance) {} request(path: string) { return this.httpClient.get(path); } } </pre>
这使我们的测试更容易,因为我们可以自信地说正确的实例(真实或模拟)已提供给控制器、服务、存储库等。
这也避免了使用像这样的东西:
// ... some bootstrap function if (process.env.NODE_ENV === 'test') { someInstance = getMockInstance() } else { someInstance = RealInstance(); } </pre>
单独关注
当您处理请求时,需要做一些事情:
- 路由(映射路由处理程序)
- 控制器(你的路由处理器)
- 服务(与 Repositories/Models/Entities 交互)
- 模型(您的
ProductModel
或数据层)你目前拥有所有这些内联(我认为我们 99.99% 的人在选择 Express 时都会这样做)。
// product.routes.ts router.get('/', ProductController.get); // pass initialised controller method // product.controller.ts class ProductController { constructor(private readonly service: ProductService) {} get(request: Request, response: Response) { // do anything with request, response (if needed) // if you need validation, try middleware response.send(await this.service.getAllProducts()); } } // product.service.ts class ProductService { // Model IProduct (gets stripped on SO) constructor(private readonly model: Model) {} getAllProducts() { return this.model.find({}); } } </pre>
测试
我们现在剩下几个可以轻松测试的组件,以确保正确的输入产生正确的输出。在我看来,jest 是最简单的模拟方法的工具之一,类,以及其他所有提供良好抽象的工具。
// product.controller.test.ts it('should call service.getAllProducts and return response', async () => { const products = []; const response = { send: jest.fn().mockResolvedValue(products), }; const mockModel = { find: jest.fn().mockResolvedValue(products), }; const service = new ProductService(mockModel); const controller = new ProductController(service); const undef = await controller.get({}, response); expect(undef).toBeUndefined(); expect(response.send).toHaveBeenCalled(); expect(response.send).toHaveBeenCalledWith(products); expect(mockModel.find).toHaveBeenCalled(); expect(mockModel.find).toHaveBeenCalledWith(); }); // product.service.test.ts it('should call model.find and return response', async () => { const products = []; const mockModel = { find: jest.fn().mockResolvedValue(products), }; const service = new ProductService(mockModel); const response = await service.getAllProducts(); expect(response).toStrictEqual(products); expect(mockModel.find).toHaveBeenCalled(); expect(mockModel.find).toHaveBeenCalledWith(); }); // integration/e2e test (app.e2e-test.ts) - doesn't run with unit tests // test everything together (mocking should be avoided here) it('should return the correct response', () => { return request(app).get('/').expect(200).expect(({body}) => { expect(body).toStrictEqual('your list of products') }); }) </pre>
对于您的应用程序,您需要确定将依赖项注入正确 类 的合适方法。您可能会决定接受所需模型的
main
函数适合您,或者可能会认为 https://www.npmjs.com/package/injection-js 等更强大的功能会起作用。避免 OOP
如果您希望避免使用对象,请接受实例作为函数参数:
productServiceGetAll(params: SomeParams, model?: ProductModel)
.了解更多
- https://www.guru99.com/unit-testing-guide.html
- https://jestjs.io/docs/mock-functions
- https://levelup.gitconnected.com/typescript-object-oriented-concepts-in-a-nutshell-cb2fdeeffe6e?gi=81697f76e257
- https://www.npmjs.com/package/supertest
- https://tomanagle.medium.com/strongly-typed-models-with-mongoose-and-typescript-7bc2f7197722