5

How would I go about stubbing S3 uploads in Node.js?

For insight, I'm using Mocha for tests and Sinon for stubbing, but I'm open to changing anything. I have a file that exports a function that performs the upload. It looks like this:

var AWS = require('aws-sdk');
var s3 = new AWS.S3({ params: { Bucket: process.env.S3_BUCKET }});
var params = { Key: key, Body: body };
s3.upload(params, function (error, data) {
  // Handle upload or error
});

If I try to stub AWS.S3 or AWS.S3.prototype, nothing changes. I assume this is because my tests have required aws-sdk themselves and have their own copy of each function.

My test looks like this:

describe('POST /files', function () {
  var url = baseURL + '/files';
  it('uploads the file to s3', function (done) {
    var fs = require('fs');
    var formData = {
      video: fs.createReadStream(process.cwd() + '/test/support/video.mp4')
    };
    var params = {url: url, formData: formData};
    request.post(params, function (error, response, body) {
      expect(response.statusCode).to.eq(200);
      expect(response.body).to.eq('Uploaded');
      done();
    });
  });
});

This test works fine, but it does not stub the upload to S3, so the upload actually goes through :X.

Baub
  • 5,004
  • 14
  • 56
  • 99
  • If it's helpful, rather than stubbing you could replace the host that the AWS SDK uses so that it uses a dummy host. There are also servers for emulating S3 that you can run locally. – Brad Oct 01 '15 at 21:08
  • @Brad That's definitely an option if stubbing doesn't work out. :/ – Baub Oct 01 '15 at 21:10

4 Answers4

1

You can stub with Sinon.js as follows if you'd like:

  1. Expose the AWS.S3 instance:

    var AWS = require('aws-sdk');
    var s3 = new AWS.S3({ params: { Bucket: process.env.S3_BUCKET }});
    var params = { Key: key, Body: body };
    exports.s3.upload(params, function (error, data) {
    
    });
    //Expose S3 instance
    exports.s3 = s3;
    
  2. Stub the same instance like so:

    var sinon = require('sinon');
    //Import module you are going to test
    var UploadService = require('./uploadService');
    
    describe('POST /files', function () {
    
      before(function() {
        //Stub before, and specify what data you'd like in the callback.
        this.uploadS3Stub = sinon.stub(uploadService.s3, 'upload').callsArgWith(1, null, { data: 'Desired response' });
      });
    
      after(function() {
        //Restore after
        this.uploadS3Stub.restore();
      });
    
      var url = baseURL + '/files';
      it('uploads the file to s3', function (done) {
        var fs = require('fs');
        var formData = {
          video: fs.createReadStream(process.cwd() + '/test/support/video.mp4')
        };
        var params = {url: url, formData: formData};
        var self = this;
    
        request.post(params, function (error, response, body) {
          expect(response.statusCode).to.eq(200);
          expect(response.body).to.eq('Uploaded');
          //You can also check whether the stub was called :)
          expect(self.uploadS3Stub.calledOnce).to.eql(true);
          done();
        });
      });
    });
    
gyamana
  • 1,202
  • 1
  • 16
  • 28
  • Old answer but worth noting it works as promised. Just one note - its sinon.stub not Sinon.stub – Liviu R May 22 '17 at 19:10
  • 1
    Hey @LiviuR, updated answer so that it is `sinon.stub`. Also maybe take a look at the answer suggested by https://stackoverflow.com/questions/26243647/sinon-stub-in-node-with-aws-sdk/28736661#28736661, as it seems to be more popular than using sinon directly – gyamana May 23 '17 at 00:54
1

There are several options to mock S3 in Node.

Some modules specific to S3:

Some modules for general AWS mocking:

And you can even start a simple server that responds to some of the S3 API calls:

The last one can easily be used in other languages and runtimes, not only in Node.

rsp
  • 107,747
  • 29
  • 201
  • 177
1
s3UploadStub = AWS.S3.prototype.upload = sandbox.stub();



 it("should  upload  successfully", async function() {
        s3UploadStub.yields(null, data);
        let response = await FileUtil.uploadFile(fileReference,data);
        expect(s3UploadStub.calledOnce).to.be.true;
        expect(response.statusCode).to.equal(200);
        expect(response.body).to.equal(fileReference);
    });
swarnim gupta
  • 213
  • 1
  • 5
0

You need to create stubs using sinon and replace the variables in the code you're testing with the stubs. There are a few modules to do this, try Mockery or Rewire. For example with Rewire you load the module you're testing using rewire instead of require then use __set__ to set the variables.

var rewire = require('rewire');
var moduleUnderTest = rewire('myModule');
moduleUnderTest.__set__('s3', stubbedS3);
piemonkey
  • 741
  • 3
  • 6