您可以在请求回调中嵌套 it() 块吗?
Can you nest it() blocks in a request callback?
您似乎可以将 "stuff" 放在 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
块放入节点 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
块都在发出相同的请求,所以我想保持干爽。
这里有两个步骤:
- 收集测试(调用
it()
)。
- 执行收集的测试。
在您的第一个示例中,您的测试已定义,同时处理步骤 1。在您的第二个示例(带回调)中,您调用 it
是在步骤 2 期间执行的,因此 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()
单个测试中的语句。
深入讲解
这需要一些来自reader的背景知识:
- 了解synchronous & asynchronous
之间的区别
- 了解 Node.js 中的 event loop 及其工作原理
- 了解接受另一个函数的函数不需要异步(使用事件循环进行回调)(例如
forEach
)
继续之前的一些事实:
- Jasmine 希望同步定义所有测试用例。在文件中调用 main
describe()
完成后,它开始执行收集的测试。
- 对
describe()
和 it()
的调用是同步的,而 HTTP 请求的执行是异步的(使用事件循环)。
describe()
为测试创建命名空间并同步调用作为第二个参数提供的函数。 it()
添加它的第二个参数作为对当前命名空间的测试。
让我们追踪您的第一个案例:
- 调用
describe()
创建命名空间 1
并同步执行 cb1()
.
- 调用
it()
同步添加测试 1a
。
- 调用
it()
同步添加测试 1b
。
cb1()
的执行以及测试收集步骤已完成。
- 开始执行收集的测试。
- 执行测试。
- 打印结果。
- 退出程序。
添加 IIFE 不会改变任何东西,因为它只是同步调用自身。
我们来追踪你的第二个案例:
- 调用
describe()
创建命名空间 1
并同步执行 cb1()
.
- 发送请求,将回调放入事件循环队列(还记得 HTTP 请求是异步的吗?)并在下一行继续执行。
cb1()
的执行以及测试收集步骤已完成。
- 开始执行收集的测试。
- 由于没有收集到测试,打印执行了 0 个测试。
- 当前调用栈执行完毕。所以从事件循环队列中取出下一个函数,也就是请求回调。
- 调用
it()
尝试添加测试 1a
,但测试的执行已经完成。
- 退出程序。
此时应该清楚您不能也不应该在异步回调中定义测试。所以你的第三个例子永远不应该写。并且永远不要问为什么它在没有任何输出的情况下完成。
但出于兴趣,我查看了 Jasmine 源代码并进行了一些调试以查看实际发生的情况。给你。
您可能注意到函数 describe()
和 it()
不是您导入的,而是由 Jasmine 自己提供的。除了为您导入这些函数之外,Jasmine 还提供了一些内部状态,供 it()
和 describe()
用来收集测试。导入测试时,此内部状态已设置,但 运行 未设置。
当您从请求回调中调用 it()
时,您是在 运行 测试阶段执行此操作,并且未设置内部集。所以它失败并出现 Error: jasmine.Suite() required
错误。此错误导致 Jasmine 立即退出而没有任何输出。
你可能会问为什么第二个例子打印结果呢?这很简单:在您的第二个示例中,您没有任何其他测试,因此在调用 it()
时结果已经打印出来。您可以通过在对 it()
的调用之间添加另一个 console.log()
调用来检查它(它永远不会被打印)。
您似乎可以将 "stuff" 放在 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
块放入节点 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
块都在发出相同的请求,所以我想保持干爽。
这里有两个步骤:
- 收集测试(调用
it()
)。 - 执行收集的测试。
在您的第一个示例中,您的测试已定义,同时处理步骤 1。在您的第二个示例(带回调)中,您调用 it
是在步骤 2 期间执行的,因此 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()
单个测试中的语句。
深入讲解
这需要一些来自reader的背景知识:
- 了解synchronous & asynchronous 之间的区别
- 了解 Node.js 中的 event loop 及其工作原理
- 了解接受另一个函数的函数不需要异步(使用事件循环进行回调)(例如
forEach
)
继续之前的一些事实:
- Jasmine 希望同步定义所有测试用例。在文件中调用 main
describe()
完成后,它开始执行收集的测试。 - 对
describe()
和it()
的调用是同步的,而 HTTP 请求的执行是异步的(使用事件循环)。 describe()
为测试创建命名空间并同步调用作为第二个参数提供的函数。it()
添加它的第二个参数作为对当前命名空间的测试。
让我们追踪您的第一个案例:
- 调用
describe()
创建命名空间1
并同步执行cb1()
. - 调用
it()
同步添加测试1a
。 - 调用
it()
同步添加测试1b
。 cb1()
的执行以及测试收集步骤已完成。- 开始执行收集的测试。
- 执行测试。
- 打印结果。
- 退出程序。
添加 IIFE 不会改变任何东西,因为它只是同步调用自身。
我们来追踪你的第二个案例:
- 调用
describe()
创建命名空间1
并同步执行cb1()
. - 发送请求,将回调放入事件循环队列(还记得 HTTP 请求是异步的吗?)并在下一行继续执行。
cb1()
的执行以及测试收集步骤已完成。- 开始执行收集的测试。
- 由于没有收集到测试,打印执行了 0 个测试。
- 当前调用栈执行完毕。所以从事件循环队列中取出下一个函数,也就是请求回调。
- 调用
it()
尝试添加测试1a
,但测试的执行已经完成。 - 退出程序。
此时应该清楚您不能也不应该在异步回调中定义测试。所以你的第三个例子永远不应该写。并且永远不要问为什么它在没有任何输出的情况下完成。
但出于兴趣,我查看了 Jasmine 源代码并进行了一些调试以查看实际发生的情况。给你。
您可能注意到函数 describe()
和 it()
不是您导入的,而是由 Jasmine 自己提供的。除了为您导入这些函数之外,Jasmine 还提供了一些内部状态,供 it()
和 describe()
用来收集测试。导入测试时,此内部状态已设置,但 运行 未设置。
当您从请求回调中调用 it()
时,您是在 运行 测试阶段执行此操作,并且未设置内部集。所以它失败并出现 Error: jasmine.Suite() required
错误。此错误导致 Jasmine 立即退出而没有任何输出。
你可能会问为什么第二个例子打印结果呢?这很简单:在您的第二个示例中,您没有任何其他测试,因此在调用 it()
时结果已经打印出来。您可以通过在对 it()
的调用之间添加另一个 console.log()
调用来检查它(它永远不会被打印)。