Trying to stub a function results in Descriptor for property is non-configurable and non-writable

问题描述 投票:0回答:4

我正在尝试编写一个单元测试,从

@aws-sdk/s3-request-presigner
包中对 getSignedUrl 函数进行存根,但是当我尝试使用
sinon
存根该函数时,我收到了错误:

  • TypeError:属性 getSignedUrl 的描述符不可配置且不可写
const s3RequestSigner = require("@aws-sdk/s3-request-presigner");
const expect = require('chai').expect;
const sinon = require('sinon')
....
it('should throw an error when getSignedUrl rejects', async function() {
  const sandbox = sinon.createSandbox();
  sandbox.stub(s3RequestSigner, "getSignedUrl").rejects("fakeUrl");
  sandbox.restore();
})

我正在使用 node.js 16 并编写 javascript 而不是 typescript。有没有办法模拟我的功能,否则我很难编写我的测试?

javascript node.js unit-testing sinon
4个回答
3
投票

我为 ES6 模块提出了以下解决方法。您可以将

getSignedUrl
包装在您自己的模块中,然后模拟该模块。这种方法应该适用于 sinon 无法模拟“不可配置和不可写”方法的任何模块。

例如:

my-s3-client-internals.js - 您的自定义包装器模块

// You'll need to import the original method, assign it to
// a new const, then export that const
import { getSignedUrl as getSignedUrl_orig } from '@aws-sdk/s3-request-presigner';

export const getSignedUrl = getSignedUrl_orig;

my-s3-client.js - getSignedUrl 的消费者

// Import the method instead from your custom file
import { getSignedUrl } from './my-s3-client-internals';

// Call it however you normally would, for example:
export const getUrl(bucket, key) {
  const command = new GetObjectCommand({ Bucket: bucket, Key: key });
  return getSignedUrl(client, command, { expiresIn: 300 });
}

my-s3-client.spec.js - 消费者模块的单元测试

import { getUrl } from './my-s3-client';
import * as clientInternals from './my-s3-client-internals';
import sinon from 'sinon';

it('does something', () => {
  // Mock the method exported from your wrapper module
  sinon.stub(clientInternals, 'getSignedUrl')
    .callsFake(async (client, command, options) => {
      return 'fake-url';
    });

  // Then call your consumer method to test
  const url = await getUrl('test-bucket', 'test-key');
  expect(url).to.equal('fake-url');
});

2
投票

所以我不会把这个作为官方答案,除非没有更好的解决方案,但这就是我的研究带来的解决方案。

issue与此相关:https://github.com/sinonjs/sinon/issues/2377

当 Object.descriptor 不可配置时,sinon 将抛出错误。

目前我找不到明显的解决方法。解决方法是使用proxyquire:

const sinon = require('sinon')
const proxyquire =  require('proxyquire')
...
it('should throw an error when getSignedUrl rejects', async function() {
    const fakeurl = 'hello world'
    const fakeURL = sinon.stub().resolves(fakeurl)
    const handler = proxyquire(
      '../../handlers/presigned_url',
      {
        '@aws-sdk/s3-request-presigner': {
          'getSignedUrl': async () => {
            return fakeURL()
          }
        }
      }
    )

然后这将解决任何你想要的

fakeurl


0
投票

另一种可能的解决方案是使用

mockery
。例如。嘲笑
uuid

import { expect } from 'chai';
import mockery from 'mockery';
import sinon from 'sinon';

describe('domain/books', () => {
  let createBook;
  let uuidStub;

  before(async () => {
    mockery.enable({
      warnOnReplace: false,
      warnOnUnregistered: false,
    });
    uuidStub = sinon.stub();
    mockery.registerMock('uuid', { v4: uuidStub });

    ({ createBook } = await import('../domain/books.js'));
  });

  afterEach(() => {
    sinon.resetHistory();
  });

  after(() => {
    sinon.restore();
    mockery.disable();
    mockery.deregisterAll();
  });

  describe('createBook', () => {
    it('should save a book and return the id', () => {
      const id = 'abc123';
      uuidStub.returns(id);

      const { id: bookId } = createBook({
        title: 'My Book',
        author: 'Jane Doe',
      });
      expect(bookId).to.equal(id);
    });
  });
});

嘲讽设置有点乏味,但图书馆救了我很多次。


0
投票

容易存根

import { createSandbox } from 'sinon';
import * as s3PresignedPost from '@aws-sdk/s3-presigned-post/dist-cjs/createPresignedPost';

const sandbox = createSandbox();

const provider = new MyProvider();


describe('Generate get signed url', () => {
    it('Error', async () => {
      const errorStub = sandbox.stub(s3RequestPresigner, 'getSignedUrl').rejects(errorObject);

      const error: Error = await provider.functionCallgetSignedUrlInside(bucketName, urlPath).catch((error) => error);
      sandbox.assert.calledOnce(errorStub);
      expect(error.message).to.eq(errorObject.message);
    });

    it('Success', async () => {
      const url = `${baseUrl}/${bucketName}-${Date.now()}`;
      const successStub = sandbox.stub(s3RequestPresigner, 'getSignedUrl').resolves(url);

      const result = await storage.functionCallgetSignedUrlInside(bucketName, urlPath);
      sandbox.assert.calledOnce(successStub);
      expect(result).to.eq(url);
    });
  });
© www.soinside.com 2019 - 2024. All rights reserved.