使用 Jest/Supertest 进行 Express/Typescript 测试

时间:2021-05-27 12:55:33

标签: typescript express jestjs supertest

我目前正在尝试测试一个快速 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

谁能告诉我这里的问题是什么?

谢谢!

1 个答案:

答案 0 :(得分:-1)

<块引用>

谁能告诉我这里的问题是什么?

在可以避免的情况下,您在单元测试中使用了 supertestsupertest 还接受您的 express 应用程序实例,并且似乎提供了 products?还是 products 是您的快速实例?您可能会发现的另一个问题是 ProductModel.find 不会被模拟,直到 after 在您使用全局实例时调用测试。

在测试时,我们可以通过在设计代码时考虑到清晰的抽象和测试,从而使我们的生活更轻松。

依赖项

在设计代码时,设计代码以接受依赖项实例作为参数/属性:


// 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);
  }
}

这使我们的测试更容易,因为我们可以自信地说正确的实例(真实或模拟)已提供给控制器、服务、存储库等。

这也避免使用以下内容:


// ... some bootstrap function
if (process.env.NODE_ENV === 'test') {
  someInstance = getMockInstance()
} else {
  someInstance = RealInstance();
}

单独的问题

当您处理请求时,需要做一些事情:

  1. 路由(映射路由处理程序)
  2. 控制器(您的路由处理程序)
  3. 服务(与存储库/模型/实体交互)
  4. 模型(您的 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({});
  }
}

测试

我们现在剩下几个可以轻松测试的组件,以确保正确的输入产生正确的输出。在我看来,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')
  });
})

对于您的应用程序,您需要确定一种合适的方式将依赖项注入到正确的类中。您可以决定接受所需模型的 main 函数适合您,或者可以决定更强大的功能(如 https://www.npmjs.com/package/injection-js)。

避免面向对象

如果您希望避免使用对象,请接受实例作为函数参数:productServiceGetAll(params: SomeParams, model?: ProductModel)

了解详情

  1. https://www.guru99.com/unit-testing-guide.html
  2. https://jestjs.io/docs/mock-functions
  3. https://levelup.gitconnected.com/typescript-object-oriented-concepts-in-a-nutshell-cb2fdeeffe6e?gi=81697f76e257
  4. https://www.npmjs.com/package/supertest
  5. https://tomanagle.medium.com/strongly-typed-models-with-mongoose-and-typescript-7bc2f7197722