简体   繁体   English

如何存根返回 promise 的 function?

[英]How to stub function that returns a promise?

I'm trying to stub a function using sinon.我正在尝试使用 sinon 对 function 进行存根。 The function has the following signature function 具有以下签名

export function getIndexDocument(
    svc: MetaHTTPService | ServiceConfig
): MetaPromise<RepoResponseResult<IndexDocument>> {

Is this the right way to sub it这是正确的方法吗

sandbox.stub(getIndexDocument).resolves({} as RepoResponseResult)

I tried that but it returns an error.我试过了,但它返回一个错误。

Here's how this function is called.这是 function 的调用方式。

I have a class called AssetsController with the following functions我有一个名为 AssetsController 的 class 具有以下功能

 public async exploreIndexDocument(): Promise<Asset | undefined> {      
    // it makes an HTTP request and returns a promise that resolves with the following info { repoId: "", assetId: "" }

     const {
            result: { assignedDirectories }
     } = await getIndexDocument(this.serviceConfig).catch(err => {
        throw new Error(`Bad repsonse`);
     });

     return {
        repoId: result.repoId;
        assetId: result.assetId
     }

}

public async function copyAsset(asset) {
   const res = await this.exploreIndexDocument();
   const repoId = res.repoId;
   return asset.copy(repoId);
}

I'm trying to test the function copyAsset, but it calls exploreIndexDocument which calls getIndexDocument.我正在尝试测试 function copyAsset,但它调用了调用 getIndexDocument 的 exploreIndexDocument。 getIndexDocument is imported at the top of the file and lives in the module @ma/http. getIndexDocument 在文件顶部导入并位于模块@ma/http 中。 getIndexDocument makes an HTTP request. getIndexDocument 发出 HTTP 请求。

How can I test copyAsset given that it calls getIndexDocument which makes an HTTP request?鉴于它调用 getIndexDocument 发出 HTTP 请求,我该如何测试它?

According to the docs , you can't stub an existing function.根据文档,您不能存根现有的 function。

You can:你可以:

// Create an anonymous sstub function
var stub = sinon.stub();

// Replaces object.method with a stub function. An exception is thrown
// if the property is not already a function.
var stub = sinon.stub(object, "method");

// Stubs all the object’s methods.
var stub = sinon.stub(obj);

What you can't do is stub just a function like:你不能做的是只存根 function 像:

var stub = sinon.stub(myFunctionHere);

This makes sense because if all you have is a reference to a function, then you can just create a new function to use instead, and then pass that into where ever your test needs it to go.这是有道理的,因为如果您所拥有的只是对 function 的引用,那么您可以创建一个新的 function 来代替使用,然后将其传递到您的测试需要它的任何地方 Z34D1F91FB2E514B8576ZFABA17


I think you just want:我想你只是想要:

const myStub = sandbox.stub().resolves({} as RepoResponseResult)

In your update it sounds like you want to put the stub on the AssetsController class.在您的更新中,听起来您想将存根放在AssetsController class 上。 See this answer for more info on that, but in this case I think you want:有关详细信息, 请参阅此答案,但在这种情况下,我认为您想要:

const myStub = sandbox
  .stub(AssetsController.prototype, 'exploreIndexDocument')
  .resolves({} as RepoResponseResult)

Now anytime an instance of AssetsController calls its exploreIndexDocument method, the stub should be used instead.现在, AssetsController的实例调用其exploreIndexDocument方法,就应该使用存根。

Playground 操场

I think most of your problems can be solved by revisiting your architecture.我认为你的大部分问题都可以通过重新审视你的架构来解决。 For example, instead of creating an explicit dependency on getIndexDocument within your AssetController class you can simply inject it in. This will allow you to swap implementations depending on the context.例如,无需在AssetController class 中创建对getIndexDocument的显式依赖,您只需将其注入即可。这将允许您根据上下文交换实现。

type IndexDocumentProvider = (svc: MetaHTTPService | ServiceConfig) => MetaPromise<RepoResponseResult<IndexDocument>>;

interface AssetControllerOptions {
  indexDocumentProvider: IndexDocumentProvider
}

class AssetController {
  private _getIndexDocument: IndexDocumentProvider;

  public constructor(options: AssetControllerOptions) {
    this._getIndexDocument = options.indexDocumentProvider;
  }
}

Then you can use this._getIndexDocument wherever and not worry about how to make the original implementation behave like you want in your tests.然后,您可以在任何地方使用this._getIndexDocument ,而不必担心如何使原始实现在测试中表现得像您想要的那样。 You can simply provide an implementation that does whatever you'd like.您可以简单地提供一个执行您想要的任何操作的实现。

describe('copyAsset', () => {
  it('fails on index document error.', () => {
    const controller = new AssetController({
      indexDocumentProvider: () => Promise.reject(new Error(':('));
    });
    ....
  });

  it('copies asset using repo id.', () => {
    const controller = new AssetController({
      indexDocumentProvider: () => Promise.resolve({ repoId: "420" })
    });
    ...
  });
});

You can obviously use stubs instead of just functions or whatever if you need something fancy.如果你需要一些花哨的东西,你显然可以使用stubs而不是函数或任何东西。

Above we removed an explicit dependency to an implementation and instead replaced it with a contract that must be provided to the controller.上面我们删除了对实现的显式依赖,取而代之的是必须提供给 controller 的合同。 The is typically called Inversion of Control and Dependency Injection通常称为Inversion of ControlDependency Injection

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM