【问题标题】:"no such table: users" when testing and using Bookshelf / Knex测试和使用 Bookshelf / Knex 时的“没有这样的表:用户”
【发布时间】:2017-06-02 13:16:02
【问题描述】:

我正在尝试使用 Mocha 和我的 Bookshelf / Knex 模型运行一个简单的测试,但是我收到错误“未处理的拒绝错误:SQLITE_ERROR:没有这样的表:用户”。请注意,我正在尝试在内存中使用 SQLite。

这是我的 Knex 文件:

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'
    }
  }

};

数据库.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');
};

【问题讨论】:

  • Bookshelf 和 Knex 都不会自动创建表格。您是否正在创建这个“用户”表(例如,使用 knex Schema Builder)?
  • 我已经使用 Knex CLI 创建了一个迁移文件,我已经用这个迁移的代码更新了问题。
  • 您通常是直接测试您的模型还是使用测试您的 API 来间接测试模型?
  • 我两者都做。我对每个 API 进行了测试,但也测试了模型级别。 API 测试的粒度低于模型测试。因此,如果一个模型暴露了一些复杂的逻辑,那么直接测试它会更容易覆盖大多数路径。
  • 太棒了。谢谢你。我将检查我正在做的测试并替换检查数据库是否为空的测试,因此不需要重新创建数据库和执行迁移

标签: node.js knex.js bookshelf.js


【解决方案1】:

首先:您的测试是错误的:count() 返回一个计数 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

【讨论】:

  • 我尝试了您关于使用await 的建议,但是它不能在async 函数之外使用,如果我执行类似(async ()=>{ await knex.migrate.latest([config]); })(); 的操作,则行为似乎与以前相同因为它说找不到表。
  • 我说在before()回调中使用await。在那里你可以有类似await migrations(); done() 的东西。当然,这意味着放弃在启动时自动调用迁移的想法,反正目前已经被打破了。
猜你喜欢
  • 1970-01-01
  • 2021-08-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-21
  • 2020-03-01
  • 1970-01-01
相关资源
最近更新 更多