使用Grunt模拟端点

时间:2013-07-29 21:33:31

标签: rest angularjs mocking gruntjs yeoman

我正在使用Yeoman,Grunt和Bower来构建一个独立于后端构建前端的平台。我的想法是我的所有(AngularJS)控制器,服务,工厂等都存在于这个项目中,然后根据grunt构建的结果注入我的服务器端代码库。

我的问题是:

如何模拟端点,以便Grunt服务器响应与我的(Rails)应用程序相同的端点?

目前我正在使用:

 angular.module('myApp', ['ngResource'])

 .run(['$rootScope', function ($rootScope) {
     $rootScope.testState = 'test';
  }]);

然后在我的每项服务中:

   mockJSON = {'foo': 'myMockJSON'}

并且在每种方法上:

   if($rootScope.testState == 'test'){
    return mockJSON;
  }
  else {
    real service logic with $q/$http goes here
  }

然后在grunt build之后删除testState = 'test'

这显然是一个相对笨拙的架构。我怎么能避免呢?我如何让Grunt响应与我的应用程序相同的端点(其中一些有动态参数)应用一些逻辑(如果需要),并提供一个json文件(可能依赖于路径参数)?

6 个答案:

答案 0 :(得分:14)

我已经通过使用express来编写一个用静态json响应的服务器来解决这个问题。

首先,我在项目中创建了一个名为'api'的目录。在该目录中,我有以下文件:

package.json

   {
     "name": "mockAPI",
     "version": "0.0.0",
     "dependencies": {
        "express": "~3.3.4"
      }
   }

然后我在此目录中运行npm install

index.js

    module.exports = require('./lib/server');

lib/server.js

    express = require('express');
    var app = express();

    app.get('/my/endpoint', function(req, res){
        res.json({'foo': 'myMockJSON'});
   });

    module.exports = app

最后是我的全球Gruntfile.js

         connect: {
            options: {
               port: 9000,
               hostname: 'localhost',
            },
            livereload: {
              options: {
                 middleware: function (connect, options) {
                   return [
                     lrSnippet,
                     mountFolder(connect, '.tmp'),
                     mountFolder(connect, yeomanConfig.app),
                     require('./api')
                   ];
               }
            }
         },

然后服务发出请求,快递服务器提供正确的JSON。

grunt build之后,快速服务器只需替换为rails服务器。

答案 1 :(得分:8)

grunt-contrib-connect v.0.7.0开始,您还可以将自定义中间件添加到现有中间件堆栈,而无需手动重建现有的中间件堆栈。

livereload: {
    options: {
        open: true,
        base: [
            '.tmp',
            '<%= config.app %>'
        ],
        middleware: function(connect, options, middlewares) {
            // inject a custom middleware into the array of default middlewares
            middlewares.push(function(req, res, next) {
                if (req.url !== '/my/endpoint') {
                    return next();
                }
                res.writeHead(200, {'Content-Type': 'application/json' });
                res.end("{'foo': 'myMockJSON'}");
            });
            return middlewares;
        }
    }
},

有关官方文档,请参阅https://github.com/gruntjs/grunt-contrib-connect#middleware

答案 2 :(得分:4)

或者,您可以使用grunt-connect-proxy将测试服务器中缺少的所有内容代理到实际的后端。

安装起来非常简单,只需记住一件事就是在向livereload连接中间件添加代理时最后添加它,如下所示:

middleware: function (connect) {
    return [
        lrSnippet,
        mountFolder(connect, '.tmp'),
        mountFolder(connect, yeomanConfig.app),
        proxySnippet
    ];
}

答案 3 :(得分:2)

grunt-connect-prism类似于Ruby项目VCR。它为前端开发人员提供了一种简单的方法来记录其API(或其他一些远程源)返回的HTTP响应,并在以后重放它们。它基本上是一个HTTP缓存,但适用于从事单页应用程序(SPA)的开发人员。 You can also generate stubs for API calls that don't exist, and populate them the way you want.

它有助于嘲笑复杂的&amp;开发期间的高延迟API调用。在为您的SPA编写e2e测试时,它也很有用,从等式中删除服务器。这样可以更快地执行您的e2e测试套件。

Prism的工作原理是将自定义连接中间件添加到grunt-contrib-connect插件提供的连接服务器中。在&#39;记录&#39;模式它将在文件系统上为每个响应生成一个文件,其内容如下:

  {
    "requestUrl": "/api/ponies",
    "contentType": "application/json",
    "statusCode": 200,
    "data": {
      "text": "my little ponies"
    }
  }

免责声明:我是这个项目的作者。

答案 4 :(得分:0)

您可以使用Apache代理并将REST服务器与gruntjs连接。

Apache会这样做: proxy / - &gt; gruntjs 代理/服务 - &gt; REST服务器


你会使用你的应用程序击中Apache而angular.js应用程序会认为这是在与自己交谈所以没有跨域问题。

这是一个很好的教程,介绍如何进行设置: http://alfrescoblog.com/2014/06/14/angular-js-activiti-webapp-with-activiti-rest/

答案 5 :(得分:0)

基于亚伯拉罕P的回答,这是我的替代方式。它不需要在'api'文件夹中安装express。我可以为某些文件分离模拟服务。例如,我的'api'文件夹包含3个文件:

API \

  • index.js //分配所有“模块”,然后只需要。
  • user.js //所有模拟用户
  • product.js //所有模拟产品

file user.js

var user = function(req, res, next) {
if (req.method === 'POST' && req.url.indexOf('/user') === 0) {
     res.end(
          JSON.stringify({
                    'id' : '5463c277-87c4-4f1d-8f95-7d895304de12',
                    'role' : 'admin'
            })
         );
    }
    else {
        next();
    }
}
module.exports = user;

file product.js

var product = function(req, res, next) {
if (req.method === 'POST' && req.url.indexOf('/product') === 0) {
     res.end(
          JSON.stringify({
                    'id' : '5463c277-87c4-4f1d-8f95-7d895304de12',
                    'name' : 'test',
                    'category': 'test'
            })
         );
    }
    else {
        next();
    }
}
module.exports = product;

index.js只分配所有“模块”,我们只需要。

module.exports = {
    product: require('./product.js'),
    user: require('./user.js')
};

我的 Gruntfile.js 文件

 connect: {
      options: {
        port: 9000,
        // Change this to '0.0.0.0' to access the server from outside.
        hostname: 'localhost',
        livereload: 35729
      },
      livereload: {
        options: {
          open: true,
          middleware: function (connect) {

            return [
              connect.static('.tmp'),
              connect().use(
                '/bower_components',
                connect.static('./bower_components')
              ),
              connect.static(appConfig.app),
              require('./api').user,
              require('./api').product,
            ];
          }
        }
      }