你可以在请求回调中嵌套it()块吗?

时间:2015-07-24 16:03:34

标签: javascript node.js jasmine jasmine-node

您似乎可以将“内容”放在describe内,但在it之外:

describe('1', function() {
  var foo = 'bar';
  console.log(foo);
  it('1a', function(done) {
    expect(true).toBe(true);
    done()
  });
  it('1b', function(done) {
    expect(true).toBe(true);
    done();
  });
});

似乎你可以将it块放在一个名为的函数中:

describe('1', function() {
  (function() {
    it('1a', function(done) {
      expect(true).toBe(true);
      done()
    });
    it('1b', function(done) {
      expect(true).toBe(true);
      done();
    });
  })()
});

在这两种情况下,输出都是:

~/code/study-buddies $ jasmine-node server
bar
..

Finished in 0.005 seconds
2 tests, 2 assertions, 0 failures, 0 skipped

但是当我尝试将it块放在Node request回调中时,它似乎没有运行测试:

describe('1', function cb1() {
  var foo = 'bar';
  console.log(foo);
  var request = require('request');
  request.get('http://localhost:3000/api/posts', function() {
    console.log('here');
    it('1a', function cb1a(done) {
      console.log('1a');
      expect(true).toBe(true);
      done();
    });
    it('1b', function cb1b(done) {
      console.log('1b');
      expect(true).toBe(true);
      done();
    });
  });
});

输出:

~/code/study-buddies $ jasmine-node server
bar


Finished in 0.007 seconds
0 tests, 0 assertions, 0 failures, 0 skipped


here

当我运行我的真实代码时:

var request = require('request');
var base_url = 'http://localhost:3000/api/posts';

describe('Posts', function() {
  var post;

  beforeEach(function(done) {
    var options = {
      url: base_url,
      method: 'POST',
      json: {
        title: 'one'
      }
    };
    request(options, function(err, response, body) {
      if (err) console.log('ERROR IN THE SET UP.');
      else { post = body; }
      done();
    });  
  });

  afterEach(function(done) {
    request.del(base_url+'/'+post._id, function(err, response, body) {
      if (err) console.log('ERROR IN THE TEAR DOWN.');
      else { post = null; }
    });
    done();
  });
  describe('GET /', function() {
    it('returns a status code of 200', function(done) {
      request.get(base_url, function(err, response, body) {
        expect(response.statusCode).toBe(200);
        done();
      });
    });
    it('returns the right posts', function(done) {
      request.get(base_url, function(err, response, body) {
        expect(JSON.parse(body)).toEqual([post]);
        done();
      });
    });
  });
  describe('GET /:id', function() {
    it('returns a status code of 200', function(done) {
      request.get(base_url+'/'+post._id, function(err, response, body) {
        expect(response.statusCode).toBe(200);
        done();
      });
    });
    it('returns the right post', function(done) {
      request.get(base_url+'/'+post._id, function(err, response, body) {
        expect(JSON.parse(body)).toEqual(post);
        done();
      });
    });
  });
  describe('POST /', function() {
    var options = {
      url: base_url,
      method: 'POST',
      json: {
        title: 'two'
      }
    };

    request(options, function(err, response, body) {
      it('returns a status code of 201', function(done) {
        expect(response.statusCode).toBe(201);
        done();
      });
      it('returns the created Post', function(done) {
        expect(body).toEqual({title: 'two'});
        done();
      });
    });
  });
  describe('PUT /:id', function() {

  });
  describe('DELETE /:id', function() {

  });
});

我没有输出:

~/code/study-buddies $ jasmine-node server
~/code/study-buddies $

为什么会这样?

注意:我正在尝试将it块嵌套在request下,因为两个it块正在发出相同的请求,所以我想要干。

1 个答案:

答案 0 :(得分:4)

这里有两个步骤:

  1. 收集测试(致电it())。
  2. 执行收集的测试。
  3. 在第一个示例中,定义了测试,同时处理步骤1.在第二个示例中(使用回调),您在{2}期间执行调用it,因此Jasmine不会处理它们。

    如何处理

    您需要定义单独的测试,或者只使用回调内的expect()来电,而无需拨打it()

    如果您需要将请求发送到同一路由,但使用不同的参数,通常需要定义单独的测试。典型示例是使用有效和无效数据测试API行为:

    describe('POST /users/', function() {
        it('should create user', function (done) {
            request.post('/users/', { /* valid data */ }, function () { /* expect clauses */ });
        });
    
        it('should respond with errors given invalid data', function (done) {
            request.post('/users/', { /* invalid data */ }, function () { /* expect clauses */ });
        });
    });
    

    当您想要测试单个请求的不同部分时,您希望在单个测试中使用多个expect()语句。例如,检查响应代码和几个参数的值:

    describe('GET /users/{id}/', function() {
        it('should return user', function (done) {
            request.get('/users/14/', function (err, response, body) { 
                expect(response.code).toBe(200);
                expect(body.username).toBe('test');
                expect(body.email).toBe('test@example.com');
            });
        });
    });
    

    显然存在一些中间案例,由您来决定每个具体案例是否足够重要,可以进行单独的测试(并复制相同的请求),或者可以通过其他expect()语句来处理在单一测试中。

    深入解释

    这需要读者的一些背景知识:

    继续之前的一些事实:

    • Jasmine希望同步定义所有测试用例。在完成对文件中的主describe()的调用之后,它开始执行收集的测试。
    • describe()it()的调用是同步的,而HTTP请求的执行是异步的(使用事件循环)。
    • describe()为测试创建命名空间,并同步调用作为第二个参数提供的函数。 it()将它的第二个参数添加为当前命名空间的测试。

    让我们追踪你的第一个案例:

    1. 致电describe()创建名称空间1并同步执行cb1()
    2. 致电it()同步添加测试1a
    3. 致电it()同步添加测试1b
    4. 完成cb1()的执行,以及测试收集步骤。
    5. 开始执行收集的测试。
    6. 执行测试。
    7. 打印结果。
    8. 退出计划。
    9. 添加IIFE不会改变任何内容,因为它只是同步调用它。

      让我们追踪你的第二个案例:

      1. 致电describe()创建名称空间1并同步执行cb1()
      2. 发送请求,将回调放到事件循环队列中(还记得HTTP请求是异步的吗?)并继续执行下一行。
      3. 完成cb1()的执行,以及测试收集步骤。
      4. 开始执行收集的测试。
      5. 由于没有收集测试,因此执行了打印0测试。
      6. 完成当前调用堆栈的执行。因此,从事件循环队列中获取下一个函数,即请求回调。
      7. 致电it()尝试添加测试1a,但测试执行已经完成。
      8. 退出计划。
      9. 此时应该清楚的是,您不能也不应该在异步回调中定义测试。所以你的第三个例子永远不应该写。并且不应该问为什么它在没有任何输出的情况下完成。

        但是出于兴趣,我查看了Jasmine源代码并进行了一些调试以查看实际发生的情况。你走了。

        您可能已经注意到函数describe()it()不是由您导入的,而是由Jasmine本身提供的。除了为您导入这些函数外,Jasmine还提供了it()describe()用于收集测试的一些内部状态。导入测试时,此内部状态已设置,但运行时则不然。

        当您从请求回调中调用it()时,您在运行测试阶段执行此操作,并且未设置内部设置。所以它失败并出现Error: jasmine.Suite() required错误。此错误导致Jasmine立即退出而没有任何输出。

        您可能会问为什么在第二个示例中打印结果呢?这很简单:在你的第二个例子中,你没有任何其他测试,所以在调用it()时,结果已经打印出来了。您可以通过在console.log()的来电之间添加另一个it()来进行检查(它永远不会被打印)。