Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 41

Supertest File Upload Testing with `.attach()`

Recommended resources

Introduction

In our last lesson, you implemented a robust validation flow for our file upload endpoint, handling cases for valid files, invalid file types, and missing files. Your API now provides clear, specific feedback for each scenario.

This lesson bridges the gap between functionality and reliability. We will write an automated integration test for this endpoint using Jest and Supertest. You'll learn the most critical pattern for this task: using the .attach() method to simulate a file upload directly in your test code. This ensures that your file upload logic remains correct and prevents future code changes from breaking it—a non-negotiable skill for production-level development and a common requirement in certification exams.


1. Preparing for the File Upload Test

Automated tests need to be self-contained and repeatable. When testing a file upload, we can't rely on a random file existing on the machine where the tests are run. The standard practice is to include a "fixture" file—a dummy file that is part of your project—specifically for testing purposes.

Let's set this up now.

Action Steps:

  1. In your project's root directory, create a new folder named tests.
  2. Inside the tests folder, create another folder named fixtures.
  3. Find a small, simple image file (e.g., a .png or .jpg) on your computer. It can be anything. Rename it to test-image.png and place it inside the tests/fixtures/ directory.
  4. Create a new text file inside tests/fixtures/ and name it test-document.txt. Add some text to it.

Your project structure should now look something like this:

/project-root
├── node_modules/
├── routes/
│   └── uploadRoutes.js
├── tests/
│   ├── fixtures/
│   │   ├── test-image.png
│   │   └── test-document.txt
│   └── ... (your other test files)
├── .env
├── .gitignore
├── app.js
└── package.json

2. Testing File Uploads with .attach()

Supertest is designed to handle all kinds of HTTP requests, including the multipart/form-data type used for file uploads. The key to this is the .attach() method.

The syntax is straightforward: .attach('fieldName', 'path/to/your/file')

  • fieldName: This is a string that must match the field name that Multer is configured to look for. In your upload.single('productImage') middleware, the field name is 'productImage'.
  • path/to/your/file: This is the path to your fixture file. To make this path reliable, we'll use Node.js's built-in path module.

Let's look at a detailed example of how this is implemented.

API Testing with Supertest and Jest - Library - Grizzly Peak Software ...

The article 'API Testing with Supertest and Jest' from Grizzly Peak Software provides an excellent, production-grade example of how to test file uploads.

Please read the section titled 'Testing File Uploads.' Focus on the first test case, 'uploads an image.' Notice three key things: It imports the path module. It constructs the file path using path.join(__dirname, 'fixtures', 'test-image.png'). __dirname is a Node.js variable that gives you the path of the current directory, ensuring your tests can be run from anywhere. It uses .attach('file', ...) to add the file to the request.

The resource How to Test Your Express APIs with Supertest also demonstrates this same pattern with the .attach() method in its "File Upload" section, confirming this is the standard approach.


3. Your Turn: Write the Upload Test

Now, it's time to apply this pattern. You will create a new test file and write a test that verifies the successful upload of a valid image.

Action Steps:

  1. In your tests/ directory, create a new file named upload.test.js.
  2. Add the following code to upload.test.js. This test simulates sending a POST request with a valid image file attached.
// tests/upload.test.js

const request = require('supertest');
const path = require('path');
const app = require('../app'); // Adjust this path if your app entry point is different

describe('File Upload API', () => {

  // Test for successful file upload
  it('should upload a valid image file and return 200', async () => {
    const filePath = path.join(__dirname, 'fixtures', 'test-image.png');

    const res = await request(app)
      .post('/api/upload') // Ensure this matches your upload route
      .attach('productImage', filePath) // 'productImage' must match your Multer config
      .expect(200);

    // Assert the response body for a success message and file information
    expect(res.body.message).toBe('File uploaded successfully!');
    expect(res.body.fileInfo).toBeDefined();
    expect(res.body.fileInfo.fieldname).toBe('productImage');
  });

  // Challenge: Now, write tests for the error cases.

  // Test for invalid file type
  it('should return 400 when uploading an invalid file type', async () => {
    const filePath = path.join(__dirname, 'fixtures', 'test-document.txt');

    const res = await request(app)
      .post('/api/upload')
      .attach('productImage', filePath)
      .expect(400);
      
    // Assert the specific error message from your fileFilter
    expect(res.body.error).toBe('Invalid file type. Only JPEG and PNG are allowed.');
  });

  // Test for request with no file attached
  it('should return 400 when no file is uploaded', async () => {
    const res = await request(app)
      .post('/api/upload')
      // Note: We are NOT calling .attach() here
      .expect(400);

    // Assert the specific error message for a missing file
    expect(res.body.error).toBe('No file was uploaded. Please select a file to upload.');
  });
});

Run Your Tests

Open your terminal and run your test command:

npm test

You should see your new upload.test.js suite execute, and all three tests should pass. This confirms that your endpoint correctly handles success, invalid types, and missing files, and that your automated tests can prove it.


Conclusion

Congratulations! You have successfully written a comprehensive set of integration tests for your file upload endpoint. You've mastered the use of Supertest's .attach() method, which is the standard for testing file uploads in Node.js applications. By creating tests for both the "happy path" (successful upload) and critical error cases, you've made your API significantly more robust and maintainable.

Key Takeaways:

  • Fixtures are Essential: Always include test files (fixtures) within your project to ensure tests are self-contained and portable.
  • Use path.join: Construct file paths using path.join(__dirname, ...) to make your tests reliable and runnable from any location.
  • Master .attach(fieldName, filePath): This is the core Supertest method for simulating file uploads in multipart/form-data requests.
  • Test All Scenarios: A complete test suite covers not just success cases but also predictable failures like invalid input or missing data.

Next Up:

Your API is now well-structured, functional, and automatically tested. The next step is to make it easy for other developers (or your exam reviewer) to use and understand. In the next lesson, we will dive into Postman. You will learn how to create a shareable Postman collection for your API, using environment variables to make your requests reusable—a key deliverable for project submissions and professional collaboration.