在Node.js上进行Yandex Alice技能的功能测试

爱丽丝,发动这项技能


自从有机会为Yandex的语音助手Alice创建技能以来,已经过去了将近一年。 新技能每天都出现在目录中 ,总数为数百。 不幸的是,有些技巧的交流要轻描淡写“不会加起来”。 该技能或者在相同的短语上循环,或者通常被打断并且不响应。


在本文中,我将考虑针对Node.js的技能编写功能性自动化测试。 这些测试的存在使您可以创建更好的技能,并对其表现充满信心。


现有测试工具


Skill for Alice是可以以特定格式响应POST请求的Web服务器。 目前,有几种工具可用于传输技能URL并检查其操作:



这些工具的独特之处在于它们提供了一些用于手动技能测试的UI。 我想要持续融合的最佳传统
在控制台中运行命令,自动检查所有脚本,然后再上传新版本。


同时,我不想深入研究各个技能模块的单元测试。 请求/响应协议记录在文档中 ,因此最好在此级别进行测试。 然后,即使完全重写了内部架构,您也不必更改测试。 也就是说,从本质上讲,这些是功能测试。


我没有找到完成此任务的Node.js库,因此我们将编写自己的:)



从GitHub上的Yandex存储库中获取官方技能示例 。 这就是技巧“鹦鹉”,它只是重复用户所说的一切。 基于框架构建,仅包含几行代码:


// server.js const micro = require('micro'); const {json} = micro; module.exports = micro(async req => { const {request, session, version} = await json(req); return { version, session, response: { text: request.original_utterance || 'Hello!', end_session: false, }, }; }); 

在第一次呼叫时,该技能将收到用户的空白消息(original_utterance),并回答"Hello!" 。 在其他情况下,它只是将用户的消息复制到response.text字段中。


我将原始的GitHub示例代码包装在micro()函数中,以便导出返回一个http服务器,我们将在测试中使用它。


测试计划


因此,要在测试中涵盖这种技能,您需要满足以下条件:


  1. 在本地端口上提升服务器技能
  2. 检查两种情况:
    • 用户输入技能后,技能必须回答“你好!”。
    • 用户向技能发送消息,技能必须以相同的消息回应
  3. 熟练地停止服务器并显示报告

通过自动执行这些检查,您可以在每次提交之前运行它们,并确保没有任何损坏。


我们将使用mocha的语法根据计划编写测试代码。 假设我们已经有一些User类可以满足我们的所有需求:


 // test.js const assert = require('assert'); before(done => { //    server.listen(PORT, done); }); it('should get hello on enter', async () => { //     const user = new User(`http://localhost:${PORT}`); //       const response = await user.enter(); //    assert.equal(response.text, 'Hello!'); }); after(done => { //   server.close(done); }); 

仍然需要编写User类,并且可以运行测试。


虚拟使用者


测试用户应该能够做的主要事情是将POST请求与所需格式的数据一起发送到技能URL。 请求格式在文档中进行了描述 。 现在我们不需要所有字段,因此我只留下了必要的内容,以免膨胀示例代码。 带有注释的User类别:


 // user.js const fetch = require('node-fetch'); module.exports = class User { /** *  * @param {String} webhookUrl */ constructor(webhookUrl) { this._webhookUrl = webhookUrl; } /** *     */ async enter() { const headers = { 'Accept': 'application/json', 'Content-Type': 'application/json' }; //    ,  -   const body = this._buildRequest(''); const response = await fetch(this._webhookUrl, { method: 'post', headers, body: JSON.stringify(body), }); const json = await response.json(); return json.response; } /** *        * @param {String} message */ _buildRequest(message) { return { request: { command: message, original_utterance: message, type: 'SimpleUtterance', }, session: { new: true, user_id: 'user-1', session_id: 'session-1' }, version: '1.0' } } }; 

发射


首先,仍然需要将用户和服务器类导入测试文件,并设置服务器将升起的端口的值:


 // test.js ... const server = require('./server'); const User = require('./user'); const PORT = 3456; ... 

安装所有必需的依赖项:


 npm install micro node-fetch mocha 

并运行测试:


 $ mocha test.js ✓ should get hello on enter 1 passing (34ms) 

一切都很好,测试已经通过!


但是在继续之前,您需要确保测试确实有效。 为此,请替换响应技能“ Hello!”。 向“你好!” 并再次运行:


 $ mocha test.js 0 passing (487ms) 1 failing 1) should get hello on enter: AssertionError [ERR_ASSERTION]: '!' == 'Hello!' + expected - actual -! +Hello! 

测试显示出错误-应当如此。
现在,可以肯定的是,我们考虑了第一种情况。


我们教用户交流


第二种情况仍然存在,即用户向技能发送消息并且必须返回相同的消息。 为了使用户可以“交流”,我在User类中添加了say(message)方法。 我还做了一些重构:我将http-requests发送到一个单独的方法,并在enter()使用了它,然后say(message)


 // user.js const fetch = require('node-fetch'); module.exports = class User { /** *  * @param {String} webhookUrl */ constructor(webhookUrl) { this._webhookUrl = webhookUrl; } /** *     */ async enter() { //    ,  -   const body = this._buildRequest(''); return this._sendRequest(body); } /** *     * @param {String} message */ async say(message) { const body = this._buildRequest(message); return this._sendRequest(body); } /** *  http- * @param {Object} body   */ async _sendRequest(body) { const headers = { 'Accept': 'application/json', 'Content-Type': 'application/json' }; const response = await fetch(this._webhookUrl, { method: 'post', headers, body: JSON.stringify(body), }); const json = await response.json(); return json.response; } // ... }; 

第二种情况的测试代码如下所示:


 it('should reply the same message', async () => { //   const user = new User(`http://localhost:${PORT}`); //    await user.enter(); //   const response = await user.say('  ?'); //    assert.equal(response.text, '  ?'); }); 

我们再次开始,我们看到两个测试都通过了:


 $ mocha test.js ✓ should get hello on enter ✓ should reply the same message 2 passing (37ms) 

进一步的步骤


以相同的方式,您可以向该技能添加更复杂的脚本,并用测试进行覆盖。 这将确保新的更改不会破旧。


创建的测试基础结构也可以得到改进:


  • 修改User类,以便可以更改请求中的其余字段(例如,选中该用户没有屏幕的框)
  • 连接代码覆盖率(例如nyc
  • 将所有检查挂在预提交/预推钩上(例如使用husky

我有几种技能,因此我将测试用户类放在一个单独的alice-tester程序包中,也许有人会派上用场。


我还从GitHub上的文章中发布了示例完整工作代码。 您可以克隆存储库并进行实验。


感谢您的关注!

Source: https://habr.com/ru/post/zh-CN441978/


All Articles