2017-05-25 137 views
0

我曾经有过下面的代码中承诺:测试承诺

function makeCall(userInfo) { 
    api.postUser(userInfo).then(response => { 
    utils.redirect(response.url); 
    }) 

    // other logic 
    return somethingElse; 
} 

,我能够写一个测试,是这样的:

const successPromise = Promise.resolve({ url: 'successUrl' }) 

beforeEach(function() { 
    sinon.stub(api.postUser).returns(successPromise); 
} 

afterEach(function() { 
    api.postUser.restore(); 
} 

it "calls API properly and redirects" do 
    makeCall({}); 
    expect(api.postUser).calledWith(userInfo).toBe(true); 
    successPromise.then(() => { 
    expect(utils.redirect.calledWith('successUrl')).toBe(true); 
    done(); 
    } 
emd 

,一切都是绿色的。现在

,我不得不添加另一个承诺,使另外一个外部调用,这样做的API postUser调用之前,所以我的代码如下所示:

function makeCall(names) { 
    fetchUserData(names).then(userData => { 
    return api.postUser(userData).then(response => { 
    utils.redirect(response.url); 
    }) 
    }) 

    // other logic 
    return somethingElse; 
} 

其中fetchUseData是许多承诺,比如像链:

function fetchNames(names) { 
    // some name regions 
    return Promise.all(names); 
} 
function fetchUserData(names) { 
    fetchUsersByNames(names).then(users => { 
    // For now we just choose first user 
    { 
     id: users[0].id, 
     name: users[0].name, 
    } 
    }); 
} 

而且我的测试失败了。我正在尝试了解如何更改我的测试,以确保我仍在测试是否正确执行了最终的API调用,并且重定向也已完成。我想存根fetchUserData(names),以防止做这个HTTP调用。

+0

您没有使用正确的承诺。你的代码没有一个'return'语句,它应该有几个(或者至少应该使用箭头函数,这样你不需要它们,你不这样做)。您不断将自由浮动的代码块称为“我的代码”。什么是调用该代码?你的测试不应该调用它吗?在您的代码已经运行到其他地方后,它们似乎正在观察一些结果。 – JLRishe

+0

尝试返回内部承诺(添加'return')以创建承诺链。 – TiagoLr

+0

@JLRishe更新。我主要关心的是我应该把“done()”放在哪里,或者我应该做什么,这样承诺就会被扼杀和评估。 –

回答

0

您没有使用正确的承诺。你的代码没有单一的return陈述,当它应该有几个(或者它应该使用箭头功能,你不需要它们,你不这样做)。

修复代码:

function makeCall(names) { 
    // v---- return 
    return fetchUserData(names).then(userData => { 
    // v---- return 
    return api.postUser(userData).then(response => { 
     utils.redirect(response.url); 
    }) 
    }) 
} 


function fetchUserData(names) { 
    // v---- return 
    return fetchUsersByNames(names).then(users => { 
    // For now we just choose first user 
    // v---- return 
    return { 
     id: users[0].id, 
     name: users[0].name, 
    } 
    }); 
} 

一旦你做到了这一点,你可以有你的测试等待所有操作完成。

测试代码:

makeCall(['name']).then(() => 
    expect(api.postUser).calledWith(userInfo).toBe(true); 
    expect(utils.redirect.calledWith('successUrl')).toBe(true); 
    done(); 
}); 
+0

嘿JLRishe,如果我不想在最外层的函数中返回承诺呢?这是一个副作用,我想返回其他的东西(原因在于这是在redux中间件中使用的,而且你总是需要返回'next(action)'。我编辑了我的问题来解释为什么我没有最后承诺的回报,而是把它当作函数的副作用 –

+0

@HommerSmith这听起来像你可能试图混合两个不兼容的范例,看起来像redux中间件有它自己的处理方式异步与promise不同,遗憾的是,我对redux中间件一无所知,但[这里有一些关于使用promise的信息](https://www.google.co.jp/search?q=redux+中间件+ promises&oq = redux +中间件+ promises&aqs = chrome..69i64.4271j0j4&sourceid = chrome&ie = UTF-8) – JLRishe

+0

感谢JLRishe,因为我的测试通过了,只要我有一个承诺(即使它没有返回)我认为,当我在最初的p中增加更多的承诺时romise,我可以弄明白。 –

0

你应该加一个return语句,否则你是不是无处返回承诺:

function fetchNames(names) { 
    // some name regions 
    return Promise.all(names); 
} 
function fetchUserData(names) { 
    return fetchUsersByNames(names).then(users => { 
    // For now we just choose first user 
    { 
     id: users[0].id, 
     name: users[0].name, 
    } 
    }); 
} 

所以,当你使用Promise.all(),那么你将有作为的承诺与所有的数组的结果所有承诺返回的值。 所以后来这个方法看起来应该是这样叫的时候:

fetchNames(names).then((arrayOfResolvedPromises) => { 
// here you will have all your promised resolved and the array holds all the results 
}); 

所以你的测试,你可以将你的里面块,其中所有的承诺将得到解决内部完成。

此外,我强烈建议您使用库作为测试承诺的承诺。 它有很多很好的方法来测试你的承诺。

https://github.com/domenic/chai-as-promised

+0

这不会让我的测试通过。如果我有几个承诺,我应该在哪里放置'done()'? –

+0

你需要在Promise.all()中保留所有的承诺。在其当前块内,你确定所有的承诺已经解决 – quirimmo