Home > front end >  How to test files and json data at the same time with jest?
How to test files and json data at the same time with jest?

Time:03-18

I have a post request with express that upload a file and some data to the 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);  
  }
}

the body must be validate by joi using the following schema:

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

Then I am creating a test to verify I am receiving a 201 status code:

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

For some reason the validation middleware throws me a 400 error saying that the data is missing:

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

I tried to send the data using .set('Accept', 'multipart/form-data') but it throws me the same error.

I guess this error has to do with the way I send the data, but I don't fully understand.

CodePudding user response:

You typically should not call a live API from a test. Instead you should mock the different possibly API response scenarios and be sure your code handles the different possibilities correctly. Ideally you'll also have a client class of some kind to place direct calls to your API inside a class that can easily be mocked.

For example, you could mock the endpoint response for valid data with something like:

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' };
    }
}

Then in your test you can mock your client call:

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

This is by no means a working test or exhaustive example, but demonstrating the idea that you really should not call your API in your tests, but instead call mock implementations of your API to handle how your client code responds in different situations.

  • Related