"no such table: users" 在测试和使用 Bookshelf / Knex 时
"no such table: users" when testing and using Bookshelf / Knex
我正在尝试 运行 使用 Mocha 和我的 Bookshelf / Knex 模型进行简单测试,但是我收到错误 "Unhandled rejection Error: SQLITE_ERROR: no such table: users"。请注意,我正在尝试在内存中使用 SQLite。
这是我的 Knexfile:
module.exports = {
development: {
client: 'sqlite3',
connection: {
filename: ':memory:'
}
},
test: {
client: 'mysql',
connection: {
host: '172.18.0.2',
user: 'root',
password: '',
database: 'staging_db',
charset : 'utf8'
},
pool: {
min: 2,
max: 10
},
migrations: {
tableName: 'knex_migrations'
}
},
production: {
client: 'mysql',
connection: {
host: process.env.DB_HOST || 'localhost',
user: process.env.DB_USER || 'usr',
password: process.env.DB_PWD || '',
database: process.env.DB_NAME || 'db',
charset : 'utf8'
},
pool: {
min: 2,
max: 10
},
migrations: {
tableName: 'knex_migrations'
}
}
};
Database.js:
var config = require('../knexfile.js');
var env = process.env.NODE_ENV || 'development';
var knex = require('knex')(config[env]);
knex.migrate.latest([config]);
let bookshelf = require('bookshelf')(knex);
bookshelf.plugin('registry'); // Resolve circular dependencies with relations
// Export bookshelf for use elsewhere
module.exports = bookshelf;
我的用户模型:
let bookshelf = require('../config/database');
require('./role');
var User = bookshelf.Model.extend({
tableName: 'users',
role: function() {
return this.hasOne(Role);
}
});
module.exports = bookshelf.model('user', User);
我的测试:
var User = require('../../models/user'),
chai = require('chai'),
expect = chai.expect;
describe('User model', function () {
it('should return empty set before adding anything', () => {
expect(User.collection().count()).to.equal(0);
});
});
我是不是漏掉了什么?
更新
正在添加迁移文件:
exports.up = function(knex, Promise) {
return knex.schema.createTable('roles', function (table) {
//this creates an id column as auto incremented primary key
table.increments();
table.string('description', 45).notNullable();
})
.createTable('users', function (table) {
table.increments();
table.string('name', 60);
table.string('password', 45);
table.integer('role_id').unsigned();
table.foreign('role_id').references('roles.id');
});
};
exports.down = function(knex, Promise) {
return knex.schema.dropTable('users').dropTable('roles');
};
首先:您的测试是错误的:count()
returns 计数 promise 而不是计数本身。因此,将您的测试更改为:
it('should return empty set before adding anything', function(done) {
User
.collection()
.count()
.then(count => {
expect(count).to.equal(0);
done()
})
.catch(err => done(err));
});
其次:您在测试和迁移之间存在竞争条件。
如果您在 knexfile.js
上启用 Knex 调试模式
//...
development: {
client: 'sqlite3',
connection: {
filename: ':memory:'
},
debug: true
},
//...
你会得到
$ ./node_modules/mocha/bin/_mocha ./test/user.js
Knex:warning - sqlite does not support inserting default values. Set the `useNullAsDefault` flag to hide this warning. (see docs http://knexjs.org/#Builder-insert).
User model
[ { sql: 'select * from sqlite_master where type = \'table\' and name = ?',
output: [Function: output],
bindings: [ 'knex_migrations' ] } ]
[ { sql: 'create table if not exists "knex_migrations" ("id" integer not null primary key autoincrement, "name" varchar(255), "batch" integer, "migration_time" datetime)',
bindings: [] } ]
{ method: 'select',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [],
__knexQueryUid: '7186321c-92b5-416c-8a5f-304118e47a0d',
sql: 'select count(*) as "count" from "users"' }
[ { sql: 'select * from sqlite_master where type = \'table\' and name = ?',
output: [Function: output],
bindings: [ 'knex_migrations_lock' ] } ]
1) should return empty set before adding anything
[ { sql: 'create table if not exists "knex_migrations_lock" ("is_locked" integer)',
bindings: [] } ]
{ method: 'select',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [],
__knexQueryUid: '7ebd63cc-1070-4422-b664-47e28b58de15',
sql: 'select * from "knex_migrations_lock"' }
0 passing (62ms)
1 failing
1) User model should return empty set before adding anything:
select count(*) as "count" from "users" - SQLITE_ERROR: no such table: users
Error: SQLITE_ERROR: no such table: users
请注意,count()
查询发生在迁移仍在检查迁移表是否存在时。
一个简单(而且过于丑陋)的修复方法是在测试中添加一些延迟:
describe('User model', function () {
this.timeout(20000);
before(function (done) {
setTimeout(() => done(), 200); // 200ms was enough on my env
})
it('should return empty set before adding anything', function (done) {
User.collection().count()
.then(count => {
expect(count).to.equal(0);
done()
})
.catch(err => done(err));
});
});
你会很高兴得到
$ ./node_modules/mocha/bin/_mocha ./test/user.js
Knex:warning - sqlite does not support inserting default values. Set the `useNullAsDefault` flag to hide this warning. (see docs http://knexjs.org/#Builder-insert).
User model
✓ should return empty set before adding anything
1 passing (225ms)
请注意,测试用了 200 多毫秒才到达 运行。
另一种可能的方法是将迁移添加到 before()
子句。这将使您能够对它们使用 await
。
我正在尝试 运行 使用 Mocha 和我的 Bookshelf / Knex 模型进行简单测试,但是我收到错误 "Unhandled rejection Error: SQLITE_ERROR: no such table: users"。请注意,我正在尝试在内存中使用 SQLite。
这是我的 Knexfile:
module.exports = {
development: {
client: 'sqlite3',
connection: {
filename: ':memory:'
}
},
test: {
client: 'mysql',
connection: {
host: '172.18.0.2',
user: 'root',
password: '',
database: 'staging_db',
charset : 'utf8'
},
pool: {
min: 2,
max: 10
},
migrations: {
tableName: 'knex_migrations'
}
},
production: {
client: 'mysql',
connection: {
host: process.env.DB_HOST || 'localhost',
user: process.env.DB_USER || 'usr',
password: process.env.DB_PWD || '',
database: process.env.DB_NAME || 'db',
charset : 'utf8'
},
pool: {
min: 2,
max: 10
},
migrations: {
tableName: 'knex_migrations'
}
}
};
Database.js:
var config = require('../knexfile.js');
var env = process.env.NODE_ENV || 'development';
var knex = require('knex')(config[env]);
knex.migrate.latest([config]);
let bookshelf = require('bookshelf')(knex);
bookshelf.plugin('registry'); // Resolve circular dependencies with relations
// Export bookshelf for use elsewhere
module.exports = bookshelf;
我的用户模型:
let bookshelf = require('../config/database');
require('./role');
var User = bookshelf.Model.extend({
tableName: 'users',
role: function() {
return this.hasOne(Role);
}
});
module.exports = bookshelf.model('user', User);
我的测试:
var User = require('../../models/user'),
chai = require('chai'),
expect = chai.expect;
describe('User model', function () {
it('should return empty set before adding anything', () => {
expect(User.collection().count()).to.equal(0);
});
});
我是不是漏掉了什么?
更新
正在添加迁移文件:
exports.up = function(knex, Promise) {
return knex.schema.createTable('roles', function (table) {
//this creates an id column as auto incremented primary key
table.increments();
table.string('description', 45).notNullable();
})
.createTable('users', function (table) {
table.increments();
table.string('name', 60);
table.string('password', 45);
table.integer('role_id').unsigned();
table.foreign('role_id').references('roles.id');
});
};
exports.down = function(knex, Promise) {
return knex.schema.dropTable('users').dropTable('roles');
};
首先:您的测试是错误的:count()
returns 计数 promise 而不是计数本身。因此,将您的测试更改为:
it('should return empty set before adding anything', function(done) {
User
.collection()
.count()
.then(count => {
expect(count).to.equal(0);
done()
})
.catch(err => done(err));
});
其次:您在测试和迁移之间存在竞争条件。
如果您在 knexfile.js
//...
development: {
client: 'sqlite3',
connection: {
filename: ':memory:'
},
debug: true
},
//...
你会得到
$ ./node_modules/mocha/bin/_mocha ./test/user.js
Knex:warning - sqlite does not support inserting default values. Set the `useNullAsDefault` flag to hide this warning. (see docs http://knexjs.org/#Builder-insert).
User model
[ { sql: 'select * from sqlite_master where type = \'table\' and name = ?',
output: [Function: output],
bindings: [ 'knex_migrations' ] } ]
[ { sql: 'create table if not exists "knex_migrations" ("id" integer not null primary key autoincrement, "name" varchar(255), "batch" integer, "migration_time" datetime)',
bindings: [] } ]
{ method: 'select',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [],
__knexQueryUid: '7186321c-92b5-416c-8a5f-304118e47a0d',
sql: 'select count(*) as "count" from "users"' }
[ { sql: 'select * from sqlite_master where type = \'table\' and name = ?',
output: [Function: output],
bindings: [ 'knex_migrations_lock' ] } ]
1) should return empty set before adding anything
[ { sql: 'create table if not exists "knex_migrations_lock" ("is_locked" integer)',
bindings: [] } ]
{ method: 'select',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [],
__knexQueryUid: '7ebd63cc-1070-4422-b664-47e28b58de15',
sql: 'select * from "knex_migrations_lock"' }
0 passing (62ms)
1 failing
1) User model should return empty set before adding anything:
select count(*) as "count" from "users" - SQLITE_ERROR: no such table: users
Error: SQLITE_ERROR: no such table: users
请注意,count()
查询发生在迁移仍在检查迁移表是否存在时。
一个简单(而且过于丑陋)的修复方法是在测试中添加一些延迟:
describe('User model', function () {
this.timeout(20000);
before(function (done) {
setTimeout(() => done(), 200); // 200ms was enough on my env
})
it('should return empty set before adding anything', function (done) {
User.collection().count()
.then(count => {
expect(count).to.equal(0);
done()
})
.catch(err => done(err));
});
});
你会很高兴得到
$ ./node_modules/mocha/bin/_mocha ./test/user.js
Knex:warning - sqlite does not support inserting default values. Set the `useNullAsDefault` flag to hide this warning. (see docs http://knexjs.org/#Builder-insert).
User model
✓ should return empty set before adding anything
1 passing (225ms)
请注意,测试用了 200 多毫秒才到达 运行。
另一种可能的方法是将迁移添加到 before()
子句。这将使您能够对它们使用 await
。