如何用jest同时测试文件和json数据?

How to test files and json data at the same time with jest?

我有一个 post 请求与快递上传一个文件和一些数据到 mongodb:

// Routes
Router.post('/api/training', validator(createVideoSchema, 'body'), uploadVideo, createVideoHandler);

// Route Handlers
async function createVideoHandler (req: Request, res: Response, next: NextFunction) {
  try {
    const dataToCreate = {
      ...req.body,
      url: req.file?.path,
      mimetype: req.file?.mimetype
    };

    const data = await service.create(dataToCreate);
    response(req, res, data, 201);
  } catch (error) {
    next(error);  
  }
}

正文必须通过 joi 使用以下模式进行验证:

import Joi from 'joi';

const title = Joi.string().email().min(5).max(255);
const description = Joi.string().min(5).max(255);
const thumbnail = Joi.string().min(5).max(255);
const tags = Joi.array().items(Joi.string().min(5).max(100));

const createVideoSchema = Joi.object({
  title: title.required(),
  description: description.required(),
  thumbnail: thumbnail.required(),
  tags: tags.required(),
});

export { createVideoSchema };

然后我正在创建一个测试来验证我收到了 201 状态代码:

it('should have a 201 status code', async () => {
    const response = await request(app).post(route)
      .set('Accept', 'application/json')
      .field('title', data.title)
      .field('description', data.description)
      .field('thumbnail', data.thumbnail)
      .field('tags', data.tags)
      .attach('video', Buffer.from('video'), { filename: 'video.mp4' });
    
    expect(response.status).toBe(201);
  });

出于某种原因,验证中间件向我抛出 400 错误,提示数据丢失:

Error: "title" is required. "description" is required. "thumbnail" is required. "tags" is required

我尝试使用 .set('Accept', 'multipart/form-data') 发送数据,但它抛出了同样的错误。

我猜这个错误与我发送数据的方式有关,但我不太明白。

您通常不应从测试中调用实时 API。相反,您应该模拟不同的可能 API 响应场景,并确保您的代码正确处理不同的可能性。理想情况下,您还将拥有某种类型的客户端 class,可以将对您的 API 的直接调用置于可以轻松模拟的 class 中。

例如,您可以模拟有效数据的端点响应,例如:

export class VideoClient {
    async createVideo(data) {
        const response = await request(app).post(route) // Whatever url points to your API endpoint
           .set('Accept', 'application/json')
           .field('title', data.title)
           .field('description', data.description)
           .field('thumbnail', data.thumbnail)
           .field('tags', data.tags)
           .attach('video', Buffer.from('video'), { filename: 'video.mp4' });
        if (response.status.ok) {
            return { response, message: 'someGoodResponseMessage'};
        }
        return { response, message: 'someErrorOccurred' };
    }
}

然后在你的测试中你可以模拟你的客户端调用:

import { VideoClient } from './clients/VideoClient.js'; // or whatever path you saved your client to
const goodData = { someValidData: 'test' };
const badData = {someBadData: 'test' };
const goodResponse = {
    response: { status: 201 },
    message: 'someGoodResponseMessage'
}
const badResponse = {
    response: { status: 400 },
    message: 'someErrorOccurred'
}

it('should have a 201 status code', async () => {
    VideoClient.createVideo = jest.fn().mockReturnValue(goodResponse);
    const results = await VideoClient.createVideo(goodData);

    expect(results.response.status).toBe(201);
    expect(results.message).toEqual('someGoodResponseMessage');
  });
it('should have a 400 status code', async () => {
    VideoClient.createVideo = jest.fn().mockReturnValue(badResponse);
    const results = await VideoClient.createVideo(badData);

    expect(results.response.status).toBe(400);
    expect(results.message).toEqual('someErrorOccurred');
  });

这绝不是一个有效的测试或详尽的示例,而是展示了这样的想法,即您真的不应该在测试中调用 API,而是调用 API 的模拟实现来处理您的客户端代码在不同情况下的响应方式。