Node.js 用Jest测试NextJSAPI中间件

Node.js 用Jest测试NextJSAPI中间件,node.js,typescript,express,jestjs,next.js,Node.js,Typescript,Express,Jestjs,Next.js,我在NextJS中有一个API路由和中间件设置,如下所示: /src/middleware/validateData/index.ts import { NextApiRequest, NextApiResponse } from 'next'; import schema from './schema'; type Handler = (req: NextApiRequest, res: NextApiResponse) => void; export default (handle

我在NextJS中有一个API路由和中间件设置,如下所示:

/src/middleware/validateData/index.ts

import { NextApiRequest, NextApiResponse } from 'next';
import schema from './schema';

type Handler = (req: NextApiRequest, res: NextApiResponse) => void;

export default (handler: Handler) => {
  return (req: NextApiRequest, res: NextApiResponse) => {
    const { error } = schema.validate(req.body, { abortEarly: false });
    if (error) res.status(400).send(error);
    else handler(req, res);
  };
};
import { NextApiRequest, NextApiResponse } from 'next';

import validateData from '../../middleware/validateData';

const foo = (req: NextApiRequest, res: NextApiResponse) => {
  res.send('It works!');
};

export default validateData(foo);
import validateData from './validateData';

describe('validateData', () => {
  const mockHandler = jest.fn();

  const mockReq = {
    body: '',
  };

  const mockRes = {
    send: jest.fn(),
  };

  it('responds with error', () => {
    validateData(mockHandler)(mockReq, mockRes);
    expect(mockRes.send).toHaveBeenCalled();
  });
});
import httpMocks from 'node-mocks-http';
import { NextApiRequest, NextApiResponse } from 'next';
import validateData from './validateData';

describe('validateData', () => {
    const mockHandler = jest.fn();
    const mockReq = httpMocks.createRequest<NextApiRequest>();
    const mockRes = httpMocks.createResponse<NextApiResponse>();

    it('responds with error', () => {
        validateData(mockHandler)(mockReq, mockRes);
        expect(mockRes.send).toHaveBeenCalled();
    });
});
/src/api/foo.ts

import { NextApiRequest, NextApiResponse } from 'next';
import schema from './schema';

type Handler = (req: NextApiRequest, res: NextApiResponse) => void;

export default (handler: Handler) => {
  return (req: NextApiRequest, res: NextApiResponse) => {
    const { error } = schema.validate(req.body, { abortEarly: false });
    if (error) res.status(400).send(error);
    else handler(req, res);
  };
};
import { NextApiRequest, NextApiResponse } from 'next';

import validateData from '../../middleware/validateData';

const foo = (req: NextApiRequest, res: NextApiResponse) => {
  res.send('It works!');
};

export default validateData(foo);
import validateData from './validateData';

describe('validateData', () => {
  const mockHandler = jest.fn();

  const mockReq = {
    body: '',
  };

  const mockRes = {
    send: jest.fn(),
  };

  it('responds with error', () => {
    validateData(mockHandler)(mockReq, mockRes);
    expect(mockRes.send).toHaveBeenCalled();
  });
});
import httpMocks from 'node-mocks-http';
import { NextApiRequest, NextApiResponse } from 'next';
import validateData from './validateData';

describe('validateData', () => {
    const mockHandler = jest.fn();
    const mockReq = httpMocks.createRequest<NextApiRequest>();
    const mockRes = httpMocks.createResponse<NextApiResponse>();

    it('responds with error', () => {
        validateData(mockHandler)(mockReq, mockRes);
        expect(mockRes.send).toHaveBeenCalled();
    });
});
模式引用是一个验证
req.body
数据的
@hapi/joi
模式,我没有包括它,因为我认为它与问题无关

我想知道如何单独对中间件进行单元测试?这大概是我所知道的:

/src/middleware/validateData/index.test.ts

import { NextApiRequest, NextApiResponse } from 'next';
import schema from './schema';

type Handler = (req: NextApiRequest, res: NextApiResponse) => void;

export default (handler: Handler) => {
  return (req: NextApiRequest, res: NextApiResponse) => {
    const { error } = schema.validate(req.body, { abortEarly: false });
    if (error) res.status(400).send(error);
    else handler(req, res);
  };
};
import { NextApiRequest, NextApiResponse } from 'next';

import validateData from '../../middleware/validateData';

const foo = (req: NextApiRequest, res: NextApiResponse) => {
  res.send('It works!');
};

export default validateData(foo);
import validateData from './validateData';

describe('validateData', () => {
  const mockHandler = jest.fn();

  const mockReq = {
    body: '',
  };

  const mockRes = {
    send: jest.fn(),
  };

  it('responds with error', () => {
    validateData(mockHandler)(mockReq, mockRes);
    expect(mockRes.send).toHaveBeenCalled();
  });
});
import httpMocks from 'node-mocks-http';
import { NextApiRequest, NextApiResponse } from 'next';
import validateData from './validateData';

describe('validateData', () => {
    const mockHandler = jest.fn();
    const mockReq = httpMocks.createRequest<NextApiRequest>();
    const mockRes = httpMocks.createResponse<NextApiResponse>();

    it('responds with error', () => {
        validateData(mockHandler)(mockReq, mockRes);
        expect(mockRes.send).toHaveBeenCalled();
    });
});
但是使用这种技术,我首先得到类型错误,即
mockReq
mockRes
缺少属性(因此我想我需要正确地模拟这些属性,但不确定如何模拟),其次测试失败,因为尽管传递了无效的主体数据,但没有调用
res.send

有人知道如何正确地模拟和测试这一点吗


我觉得我的方法是完全错误的,因为我想检查整个响应(状态代码、收到的特定消息等等)。是启动模拟服务器并实际模拟api调用或其他内容的唯一方法吗?

在这种情况下,您可以使用
节点模拟http

/src/middleware/validateData/index.test.ts

import { NextApiRequest, NextApiResponse } from 'next';
import schema from './schema';

type Handler = (req: NextApiRequest, res: NextApiResponse) => void;

export default (handler: Handler) => {
  return (req: NextApiRequest, res: NextApiResponse) => {
    const { error } = schema.validate(req.body, { abortEarly: false });
    if (error) res.status(400).send(error);
    else handler(req, res);
  };
};
import { NextApiRequest, NextApiResponse } from 'next';

import validateData from '../../middleware/validateData';

const foo = (req: NextApiRequest, res: NextApiResponse) => {
  res.send('It works!');
};

export default validateData(foo);
import validateData from './validateData';

describe('validateData', () => {
  const mockHandler = jest.fn();

  const mockReq = {
    body: '',
  };

  const mockRes = {
    send: jest.fn(),
  };

  it('responds with error', () => {
    validateData(mockHandler)(mockReq, mockRes);
    expect(mockRes.send).toHaveBeenCalled();
  });
});
import httpMocks from 'node-mocks-http';
import { NextApiRequest, NextApiResponse } from 'next';
import validateData from './validateData';

describe('validateData', () => {
    const mockHandler = jest.fn();
    const mockReq = httpMocks.createRequest<NextApiRequest>();
    const mockRes = httpMocks.createResponse<NextApiResponse>();

    it('responds with error', () => {
        validateData(mockHandler)(mockReq, mockRes);
        expect(mockRes.send).toHaveBeenCalled();
    });
});
从“节点模拟http”导入httpmock;
从“下一步”导入{NextApiRequest,nextapireresponse};
从“/validateData”导入validateData;
描述('validateData',()=>{
const mockHandler=jest.fn();
const mockReq=httpMocks.createRequest();
const mockRes=httpMocks.createResponse();
它('以错误响应',()=>{
validateData(mockHandler)(mockReq、mockRes);
expect(mockRes.send).tohavebeincall();
});
});

您可以在这样的情况下使用
节点模拟http

/src/middleware/validateData/index.test.ts

import { NextApiRequest, NextApiResponse } from 'next';
import schema from './schema';

type Handler = (req: NextApiRequest, res: NextApiResponse) => void;

export default (handler: Handler) => {
  return (req: NextApiRequest, res: NextApiResponse) => {
    const { error } = schema.validate(req.body, { abortEarly: false });
    if (error) res.status(400).send(error);
    else handler(req, res);
  };
};
import { NextApiRequest, NextApiResponse } from 'next';

import validateData from '../../middleware/validateData';

const foo = (req: NextApiRequest, res: NextApiResponse) => {
  res.send('It works!');
};

export default validateData(foo);
import validateData from './validateData';

describe('validateData', () => {
  const mockHandler = jest.fn();

  const mockReq = {
    body: '',
  };

  const mockRes = {
    send: jest.fn(),
  };

  it('responds with error', () => {
    validateData(mockHandler)(mockReq, mockRes);
    expect(mockRes.send).toHaveBeenCalled();
  });
});
import httpMocks from 'node-mocks-http';
import { NextApiRequest, NextApiResponse } from 'next';
import validateData from './validateData';

describe('validateData', () => {
    const mockHandler = jest.fn();
    const mockReq = httpMocks.createRequest<NextApiRequest>();
    const mockRes = httpMocks.createResponse<NextApiResponse>();

    it('responds with error', () => {
        validateData(mockHandler)(mockReq, mockRes);
        expect(mockRes.send).toHaveBeenCalled();
    });
});
从“节点模拟http”导入httpmock;
从“下一步”导入{NextApiRequest,nextapireresponse};
从“/validateData”导入validateData;
描述('validateData',()=>{
const mockHandler=jest.fn();
const mockReq=httpMocks.createRequest();
const mockRes=httpMocks.createResponse();
它('以错误响应',()=>{
validateData(mockHandler)(mockReq、mockRes);
expect(mockRes.send).tohavebeincall();
});
});
是一个软件包(免责声明:我创建了!),它简化了编写下一个API路由的单元测试。它使用UndertheHood生成真正的HTTP响应。例如:

import validateData from './validateData';
import { testApiHandler } from 'next-test-api-route-handler';

describe('validateData', () => {
  it('responds with error', async () => {
    await testApiHandler({
      handler: validateData((_, res) => res.send('It works!')),
      test: async ({ fetch }) => {
        // Returns a real ServerResponse instance
        const res = await fetch();
        // Hence, res.status == 200 if send(...) was called above
        expect(res.status).toBe(200);
        // We can even inspect the data that was returned
        expect(await res.text()).toBe('It works!');
      }
    });
  });
});
这样,您还可以在测试中直接检查获取的响应对象。更好的是,您的API路由处理程序将以与Next.js中相同的方式运行,因为它们传递的是实际实例,而不是类型脚本类型或模拟

可以找到更多的例子。

是一个软件包(免责声明:我创建了!),它简化了编写下一个API路由的单元测试。它使用UndertheHood生成真正的HTTP响应。例如:

import validateData from './validateData';
import { testApiHandler } from 'next-test-api-route-handler';

describe('validateData', () => {
  it('responds with error', async () => {
    await testApiHandler({
      handler: validateData((_, res) => res.send('It works!')),
      test: async ({ fetch }) => {
        // Returns a real ServerResponse instance
        const res = await fetch();
        // Hence, res.status == 200 if send(...) was called above
        expect(res.status).toBe(200);
        // We can even inspect the data that was returned
        expect(await res.text()).toBe('It works!');
      }
    });
  });
});
这样,您还可以在测试中直接检查获取的响应对象。更好的是,您的API路由处理程序将以与Next.js中相同的方式运行,因为它们传递的是实际实例,而不是类型脚本类型或模拟

可以找到更多的例子