SailsJS:如何正确地对控制器进行单元测试?

2024-05-02

一直在使用 Sails.js,但在为控制器进行 Jasmine 单元测试时遇到了困难。如果这是显而易见的事情,请原谅我的无知,因为我在过去的 3-4 个月里才深入研究 JavaScript 开发。

在过去的框架(特别是 ASP .Net MVC)中,我们有库来模拟控制器可能必须的任何依赖项,例如外部服务(通过依赖项注入)。我有点想与 Sails.js 实现相同级别的单元可测试性,以便我们实现适当的“单元”测试。具体来说,对于我的情况,我有一个依赖于服务对象的控制器操作——我只是想模拟该服务的响应。

然而,我很难让这个 Jasmine 单元测试运行(使用 jasmine-node 插件)。我的控制器及其单元测试的代码如下。我现在得到的是:

  1. The app对象似乎没有在 afterEach() 中解析
  2. 对间谍和测试级变量的断言失败。

我在单元测试中是否明显遗漏了任何明显的内容?代码如下。感谢您的任何意见!

用户控制器.js

var Battlefield4Service = require('../services/battlefield4Service');
module.exports = {
    /**
     * /user/bf4stats
     */
    bf4Stats: function (req, res) {
        var userName = req.param('userName');
        var platform = req.param('platform');
        var service = new Battlefield4Service();
        service.getPlayerInfo(userName, platform,
            function (data) {
                // Success callback
                res.json(data);
            });
    }
};

UserController.spec.js

var Sails = require('sails');
var userController = require('./UserController');
var FPSStatsDTO = require('../dto/fpsStatsDTO');

describe('UserController', function() {

    // create a variable to hold the instantiated sails server
    var app, req, res, rawObject, json;

    // Setup mocked dependencies
    beforeEach(function() {

        // Lift Sails and start the server
        Sails.lift({
            log: {
                level: 'error'
            }
        }, function(err, sails) {
            app = sails;
            //done(err, sails);
        });

        // Mocked Battlefield4Service
        Battlefield4Service = {
            getPlayerInfo:  function (userName, platform, success) {
                var dto = new FPSStatsDTO();
                dto.userName = userName;
                dto.platform = platform;
                success(dto);
            }
        };

        // req and res objects, mock out the json call
        req = {
            param: function(paramName) {
                switch (paramName) {
                    case 'userName':
                        return 'dummyUser';
                    case 'platform':
                        return 'dummyPlatform';
                }
            }
        };
        res = {
            json: function(object) {
                rawObject = object;
                json = JSON.stringify(object);
                return json;
            }
        };

        // Deploy 007
        spyOn(req, 'param');
        spyOn(res, 'json');
        spyOn(Battlefield4Service, 'getPlayerInfo');
    });

    afterEach(function(){
        app.lower();
    });

    it('Should call the Battlefield 4 Service', function() {

        // Call the controller
        userController.bf4Stats(req, res);

        // Assertions
        expect(req.param).toHaveBeenCalled();
        expect(res.json).toHaveBeenCalled();
        expect(Battlefield4Service.getPlayerInfo).toHaveBeenCalledWith(req.param('userName'), req.param('platform'));
        expect(rawObject.userName).toEqual(req.param('userName'));
        expect(rawObject.platform).toEqual(req.param('platform'));
        expect(json).toNotBe(null);
        expect(json).toNotBe(undefined);
    });
});

UPDATE

进一步思考应用程序架构,我不需要测试 Sails.js 控制器的请求/响应——在此应用程序的上下文中,控制器非常愚蠢,因为它们只是传递 JSON 对象。因此,我真正需要测试的是我的服务正在将外部 API 的对象转换为应用程序的内部 DTO,该内部 DTO 将用作 JSON 返回。换句话说,对我来说,测试实际的翻译比确保控制器通过它更重要,我们可以放心地假设情况总是如此。

话虽这么说,我将我的单元测试套件从 Jasmine 切换到 Chad 建议的 Mocha、Chai 和 Sinon 组合。在我看来,Mocha 中的异步钩子看起来更干净。我使用的一个添加库是Nock https://github.com/pgte/nock,一个旨在模拟 HTTP 请求的库,以便我可以拦截我的服务类对 API 的调用并返回一个存根对象。

因此,回顾一下,我放弃了对控制器进行单元测试,因为它对于我的用例来说是多余的。我需要测试的重要功能是将外部 API 的对象转换为我的内部应用程序的等效 DTO。

下面针对实际服务进行单元测试。请注意,这个特定的测试不需要 Sinon 进行存根/模拟,因为 Nock 为我处理了这个问题:

var Sails = require('sails');
var sinon = require('sinon'); // Mocking/stubbing/spying
var assert = require('chai').assert; // Assertions
var nock = require('nock'); // HTTP Request Mocking
var constants = require('../constants/externalSystemsConstants');
var Battlefield4Service = require('./battlefield4Service');

describe('External Services', function () {

    // create a variable to hold the instantiated sails server
    var app, battlefield4Service;

    // Global before hook
    before(function (done) {

        // Lift Sails and start the server
        Sails.lift({

            log: {
                level: 'error'
            }

        }, function (err, sails) {
            app = sails;
            done(err, sails);
        });
    });

    // Global after hook
    after(function (done) {
        app.lower(done);
    });

    describe('Battlefield 4 Service', function () {
        var userName, platform, kills, skill, deaths, killAssists, shotsHit, shotsFired;

        before(function () {

            // Mock data points
            userName = 'dummyUser';
            platform = 'ps3';
            kills = 200;
            skill = 300;
            deaths = 220;
            killAssists = 300;
            shotsHit = 2346;
            shotsFired = 7800;

            var mockReturnJson = {
                player: {
                    name: userName,
                    plat: platform
                },
                stats: {
                    kills: kills,
                    skill: skill,
                    deaths: deaths,
                    killAssists: killAssists,
                    shotsHit: shotsHit,
                    shotsFired: shotsFired
                }
            };

            // Mock response from BF4 API
            battlefield4Service = nock('http://' + constants.BF4_SERVICE_URI_HOST)
                .get(constants.BF4_SERVICE_URI_PATH.replace('[platform]', platform).replace('[name]', userName))
                .reply(200, mockReturnJson);
        });

        it('Should translate BF4 API data to FPSStatsDTO', function (done) {
            var service = new Battlefield4Service();
            service.getPlayerInfo(userName, platform, function (fpsStats) {
                assert(fpsStats !== null);
                assert(fpsStats !== undefined);
                assert(fpsStats.kills === kills, 'kills');
                assert(fpsStats.deaths === deaths, 'deaths');
                assert(fpsStats.killAssists === killAssists, 'deaths')
                assert(fpsStats.kdr === kills / deaths, 'kdr');
                assert(fpsStats.shotsFired === shotsFired, 'shotsFired');
                assert(fpsStats.shotsHit === shotsHit, 'shotsHit');
                assert(fpsStats.shotsAccuracy === shotsHit / shotsFired, 'shotsAccuracy');
                assert(fpsStats.userName === userName, 'userName');
                assert(fpsStats.platform === platform, 'platform');
                done();
            });
        });
    });
});
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SailsJS:如何正确地对控制器进行单元测试? 的相关文章

随机推荐

  • Java 安全管理器完全禁用反射

    我在 Stackoverflow 上阅读了很多关于这个问题的问题 但无法停止找到我的问题的解决方案或答案 如果已经有一个 如果有人给出提示 我将不胜感激 我的问题是是否可以完全禁用不可信代码的反射 功能类似于getDeclaredMetho
  • CSV 损坏,如何修复?

    我正在尝试解析 CSV 我想将它放入数据库或只是用 JavaScript 解析它 但由于语法损坏 任何一种方法都会失败 我的整个 CSV 文件在这里 https gist github com 1023560 https gist gith
  • RTIMER_NOW() 和clock_time() 之间的Contiki 区别

    我想知道之间的区别 RTIMER NOW and clock time 功能 我可以将它们返回的值存储在 int 变量中吗 它们返回的是整个模拟的时间还是调用它们的单个节点的时间 如果一个节点在模拟中第一个事件发生后 5 秒启动其主进程 这
  • 如何在谷歌地图的边缘创建填充

    我有一个非常繁忙的谷歌地图应用程序 我正在尝试在地图的外边缘周围创建一个 缓冲区 以便谷歌地图命令不会把东西放在那里 我的解决方案是创建不可见的 div 并将它们作为控件添加到地图中 每个边缘一个 这似乎很有效 因为所有谷歌命令都会看到它们
  • 无法覆盖 Rustup 工具链以自定义构建 iOS 工具链

    我正在用我的 Rust 版本创建我自己的工具链 我需要它与 iOS 架构进行交叉编译 当尝试设置默认工具链或覆盖当前目录的工具链时 我收到有关工具链名称的错误 以下是我创建这个新工具链所采取的步骤 创建 Rustup 工具链 rustup
  • Twitter Bootstrap 2:如何获得响应式设计以将侧边栏放在底部而不是顶部?

    Twitter 的 Bootstrap 2 http twitter github com bootstrap 最后添加了原生响应式设计 但是 默认情况下 当浏览器宽度低于最小宽度时 它将侧边栏放在顶部 我可以看到这对于许多网站来说是如何工
  • 执行 rebase 后,Git 提交会在同一分支中重复

    我理解 Pro Git 中提出的场景是关于变基的危险 https git scm com book en v2 Git Branching Rebasing rebase peril 作者基本上告诉你如何避免重复提交 不要对已推送到公共存储
  • Flex,连续扫描流(来自套接字)。我是否错过了使用 yywrap() 的某些内容?

    使用 Flex 进行模式识别 在基于套接字的扫描仪 连续流 上工作 Flex 找不到与 数组边界 重叠的匹配项 所以我实现了 yywrap 来设置新的数组内容 一旦 yylex 检测到 它将调用 yywrap 到目前为止还没有成功 基本上
  • Linux下对多个文件进行排序

    我有多个 很多 文件 每个都非常大 file0 txt file1 txt file2 txt 我不想将它们合并到一个文件中 因为生成的文件将超过 10 场演出 每个文件中的每一行都包含一个 40 字节的字符串 现在字符串的排序相当好 大约
  • 更快的第二好 MST 算法?

    我正在为此苦苦挣扎 我们可以使用 Kruskal 算法或 Prim 算法得到 MST 对于 第二好的 MST 我可以 首先使用上述任一算法获取 MST 对于来自 MST 的最优边缘的每个 V 1 A 首先删除或标记边缘b 继续计算 MST
  • 尝试使 Tableview 可点击时发生 JavaFX 错误

    我正在尝试使表格视图可单击 它将返回单击的单元格中的文本 尝试在 Netbeans 中编译时收到两个错误 所有代码均取自 示例12 11 单元格编辑的替代解决方案 官方表格视图教程 http docs oracle com javafx 2
  • 静态/动态类型与静态/动态绑定

    大家这4个术语有什么区别 能举个例子吗 Static and dynamic是指解决某些编程元素的时间点的行话 Static表示解析是在程序构建时发生的 Dynamic表示解析是在程序运行时发生的 静态和动态打字 Typing指由于数据值之
  • Spring webflux 中 Mono> 与 Flux 的区别

    我的理解是Mono
  • 在 Android 设备/模拟器上加载 NPAPI 插件

    我编译了从这个链接获得的示例 NPAPI 插件http code google com p npapi sdk http code google com p npapi sdk 使用NDK编译后 我得到了 so文件libs armeabi
  • 速度事件处理程序

    在速度中 当你执行 object variable 时 如果它无法找到 getter 函数 访问它或者 getter 返回 null 它只会在页面上显式显示 object variable 我知道有一个安静的参考 但我不想添加 对数千个变量
  • 针对 Laravel 后端的 Angular Auth

    我正在使用 Laravel 创建一个应用程序 并构建一个小型内部 API 来连接到 Angular 前端 我的身份验证工作正常 但想确保这是一种可接受的用户登录方式 并确保一切都是安全的 会话控制器 public function inde
  • XP 和 Vista 之间使用 Winsock API 的 UDP 多播差异

    Windows XP 和 Windows Vista 之间设置 UDP 多播套接字所需的实现似乎有所不同 具体来说 在 Windows XP 下 您must call bind 在您可以引用任何特定于多播的套接字选项之前 然而 在windo
  • sqlite:获取所有行的最快方法(连续磁盘访问)

    我想使用 system data sqlite 读取表中的所有行 由于我有一个非常大的表 gt 450GB 超过 60 亿行 我想确保 sqlite 将使用连续的磁盘访问 正如您可能知道的那样 对硬盘的随机访问速度很慢 由于内存限制 我无法
  • 上传大文件(几 GB)时,nginx 返回内部服务器错误

    我在 nginx 后面有一个 Artifactory 上传大于 4 GB 的文件失败 我相当确定这是 nginx 的错误 因为如果文件从本地主机上传 上传到本地主机 则不会出现问题 nginx 设置为client max body size
  • SailsJS:如何正确地对控制器进行单元测试?

    一直在使用 Sails js 但在为控制器进行 Jasmine 单元测试时遇到了困难 如果这是显而易见的事情 请原谅我的无知 因为我在过去的 3 4 个月里才深入研究 JavaScript 开发 在过去的框架 特别是 ASP Net MVC