How to unit test with a file upload in mocha

I have an app built on Express.js and I'd like to test the file upload functionality. I'm trying to reproduce the object parsed to req.files (when using express.bodyParser middleware). How can I do this?

Here is an example of how you would do it with supertest module.

var should = require('should'),
    supertest = require('supertest');
var request = supertest('localhost:3000');

describe('upload', function() {
    it('a file', function(done) {
       request.post('/your/endpoint')
              .field('extra_info', '{"in":"case you want to send json along with your file"}')
              .attach('image', 'path/to/file.jpg')
              .end(function(err, res) {
                  res.should.have.status(200) // 'success' status
                  done()
              });
    });
});

You can do this directly in Mocha but it's a bit tricky. Here's an example posting an image:

var filename = 'x.png'
  , boundary = Math.random()

request(app)
  .post('/g/' + myDraftGallery._id)
  .set('Content-Type', 'multipart/form-data; boundary=' + boundary)
  .write('--' + boundary + '\r\n')
  .write('Content-Disposition: form-data; name="image"; filename="'+filename+'"\r\n')
  .write('Content-Type: image/png\r\n')
  .write('\r\n')
  .write(fs.readFileSync('test/'+filename))
  .write('\r\n--' + boundary + '--')
  .end(function(res){
    res.should.have.status(200)
    done()
  })

The name parameter of Content-Disposition is what your file will be accessible as via req.files (so req.files.image for my example) You can also use an array value like this: name="images[]" and your file(s) will be available via an array, e.g: req.files.images[0]

Also if you're not already using it you should have a look at this (makes mocha/express testing a ~bit~ easier): https://github.com/visionmedia/express/blob/master/test/support/http.js

Edit: Since express 3-beta5, express uses supertest. To look at the old http.js code look here: https://github.com/visionmedia/express/blob/3.0.0beta4/test/support/http.js Or simply move over to supertest..

Just came across this module by TJ: https://github.com/visionmedia/supertest.

var expect = require('expect.js');
supertest = require('supertest');
var request = supertest('localhost:3000');

describe('create album', function () {
    it('valid ', function (done) {
        request.post('/albums')
            .set('Authorization', 'Token eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.IjkxMTg3NTk1ODg2MCI.gq32xfcOhv5AiZXJup5al1DGG0piyGWnrjZ5NouauCU')
            .field('Content-Type', 'multipart/form-data')
            .field('name', 'moni')
            .field('description', 'Nature+Pics')
          `enter code here`  .field('caption', 'nature')
            .field('contacts', '["' + 911354971564 + '","' + 919092888819 + '"]')
            .field('dimensions', '{"photo1":{"height": 10, "width": 10}, "photo2":{"height": 20, "width": 20}, "photo3":{"height": 20, "width": 20}, "photo4":{"height": 20, "width": 20}, "photo5":{"height": 20, "width": 20}}')
            .attach('photo1', '/home/monica/Desktop/pic/1.jpeg')
            .attach('photo2', '/home/monica/Desktop/pic/2.jpeg')
            .attach('photo3', '/home/monica/Desktop/pic/3.jpeg')
            .attach('photo4', '/home/monica/Desktop/pic/4.jpeg')
            .attach('photo5', '/home/monica/Desktop/pic/5.jpeg')
            .end(function (err, res) {
            if (err) {
                console.log(err);
            } else expect(res.status).to.equal(200);
            done();
        });
    });

});

Changing attach('image') to attach('file') will solve the problem of req.files.file not defined.

var should = require('should'),
    supertest = require('supertest');
var request = supertest('localhost:3000');

describe('upload', function() {
    it('a file', function(done) {
       request.post('/your/endpoint')
              .field('extra_info', '...')
              .attach('file', 'path/to/file.jpg')
              .end(function(err, res) {
                  res.should.have.status(200) // 'success' status
                  done()
              });
    });
});

You might try using zombie.js https://github.com/assaf/zombie , it creates a virtual browser for testing with basic functionality. It can attach a file to a specific input field and supports cookies and sessions

related gist : https://gist.github.com/764536