如何存根 returns 一个承诺的功能?
How to stub function that returns a promise?
我正在尝试使用 sinon 存根函数。该函数具有以下签名
export function getIndexDocument(
svc: MetaHTTPService | ServiceConfig
): MetaPromise<RepoResponseResult<IndexDocument>> {
这是正确的子方法吗
sandbox.stub(getIndexDocument).resolves({} as RepoResponseResult)
我试过了,但是 returns 出错了。
下面是这个函数的调用方式。
我有一个名为 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);
}
我正在尝试测试函数 copyAsset,但它调用了 exploreIndexDocument,后者又调用了 getIndexDocument。 getIndexDocument 在文件顶部导入并存在于模块@ma/http 中。
getIndexDocument 发出 HTTP 请求。
如果 copyAsset 调用发出 HTTP 请求的 getIndexDocument,我该如何测试它?
根据 to the docs,您不能存根现有函数。
您可以:
// 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);
你不能做的只是存根函数,如:
var stub = sinon.stub(myFunctionHere);
这是有道理的,因为如果您拥有的只是对函数的引用,那么您只需创建一个新函数来代替使用,然后将其传递到测试需要它的地方。
我想你只是想要:
const myStub = sandbox.stub().resolves({} as RepoResponseResult)
在您的更新中,您似乎想将存根放在 AssetsController
class 上。 See this answer 了解更多信息,但在这种情况下,我认为您需要:
const myStub = sandbox
.stub(AssetsController.prototype, 'exploreIndexDocument')
.resolves({} as RepoResponseResult)
现在只要 AssetsController
的实例调用它的 exploreIndexDocument
方法,就应该使用存根。
我认为您的大部分问题都可以通过重新访问您的架构来解决。例如,不是在 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;
}
}
然后您可以在任何地方使用 this._getIndexDocument
而不必担心如何使原始实现在测试中表现得像您想要的那样。您可以简单地提供一个执行任何您想要的实现。
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" })
});
...
});
});
如果你需要一些花哨的东西,你显然可以使用 stubs
而不仅仅是函数或其他任何东西。
上面我们删除了对实现的显式依赖,取而代之的是必须提供给控制器的契约。通常称为 Inversion of Control
和 Dependency Injection
我正在尝试使用 sinon 存根函数。该函数具有以下签名
export function getIndexDocument(
svc: MetaHTTPService | ServiceConfig
): MetaPromise<RepoResponseResult<IndexDocument>> {
这是正确的子方法吗
sandbox.stub(getIndexDocument).resolves({} as RepoResponseResult)
我试过了,但是 returns 出错了。
下面是这个函数的调用方式。
我有一个名为 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);
}
我正在尝试测试函数 copyAsset,但它调用了 exploreIndexDocument,后者又调用了 getIndexDocument。 getIndexDocument 在文件顶部导入并存在于模块@ma/http 中。 getIndexDocument 发出 HTTP 请求。
如果 copyAsset 调用发出 HTTP 请求的 getIndexDocument,我该如何测试它?
根据 to the docs,您不能存根现有函数。
您可以:
// 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);
你不能做的只是存根函数,如:
var stub = sinon.stub(myFunctionHere);
这是有道理的,因为如果您拥有的只是对函数的引用,那么您只需创建一个新函数来代替使用,然后将其传递到测试需要它的地方。
我想你只是想要:
const myStub = sandbox.stub().resolves({} as RepoResponseResult)
在您的更新中,您似乎想将存根放在 AssetsController
class 上。 See this answer 了解更多信息,但在这种情况下,我认为您需要:
const myStub = sandbox
.stub(AssetsController.prototype, 'exploreIndexDocument')
.resolves({} as RepoResponseResult)
现在只要 AssetsController
的实例调用它的 exploreIndexDocument
方法,就应该使用存根。
我认为您的大部分问题都可以通过重新访问您的架构来解决。例如,不是在 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;
}
}
然后您可以在任何地方使用 this._getIndexDocument
而不必担心如何使原始实现在测试中表现得像您想要的那样。您可以简单地提供一个执行任何您想要的实现。
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" })
});
...
});
});
如果你需要一些花哨的东西,你显然可以使用 stubs
而不仅仅是函数或其他任何东西。
上面我们删除了对实现的显式依赖,取而代之的是必须提供给控制器的契约。通常称为 Inversion of Control
和 Dependency Injection