【问题标题】:Not null validation error on a property that shouldn't exist不应该存在的属性上的非空验证错误
【发布时间】:2015-12-09 12:32:39
【问题描述】:

我正在为使用 Sequelize 的节点服务器编写单元测试。插入一些虚假数据后,我收到错误

SequelizeValidationError: notNull Violation: QuestionId cannot be null

注意大小写:QuestionId上的大写 Q

单元测试:

describe('answerQuestion', () => {
    it('should insert an answer and get the next question', (done) => {
        Survey.DBModel.create({lookType: 0}, {logging: false}).then(() => {
            Question.DBModel.create({type: 0, text: 'Test question'}, {logging: false}).then(q1 => {
                Question.DBModel.create({type: 1, text: 'Next question'}, {logging: false}).then(q2 => {
                    console.log('before');
                    QuestionOption.DBModel.create({text: 'Test option', questionId: 1, nextQuestionId: 2}, {logging: false}).then(() => {
                        console.log('after');
                        Survey.answerQuestion(1, 1, 1).then(question => {
                            question.should.have.property('id');
                        }, done);
                    }, done);
                }, done);
            }, done);
        }, done);
    });
});

控制台输出“之前”,但在到达“之后”之前出现错误

question.js

'use strict';
module.exports = function(sequelize, DataTypes) {
  var Question = sequelize.define('Question', {
    id: {
      allowNull: false,
      autoIncrement: true,
      primaryKey: true,
      type: DataTypes.INTEGER
    },
    type: {
      allowNull: false,
      type: DataTypes.INTEGER
    },
    text: {
      allowNull: false,
      type: DataTypes.STRING
    },
    nextQuestionId: {
      type: DataTypes.INTEGER
    }
  }, {
    classMethods: {
      associate: function(models) {
        models.Question.belongsTo(models.Question, {as: 'nextQuestion', foreignKey: {field: 'nextQuestionId', allowNull: true}});
        models.Question.hasMany(models.Answer, {as: 'answers', foreignKey: {field: 'questionId', allowNull: false}});
        models.Question.hasMany(models.QuestionOption, {as: 'options', foreignKey: {field: 'questionId', allowNull: false}});
        models.Question.hasMany(models.QuestionOption, {as: 'referrers', foreignKey: {field: 'nextQuestionId', allowNull: true}});
      }
    }
  });
  return Question;
};

questionoption.js

'use strict';
module.exports = function(sequelize, DataTypes) {
  var QuestionOption = sequelize.define('QuestionOption', {
    id: {
      allowNull: false,
      autoIncrement: true,
      primaryKey: true,
      type: DataTypes.INTEGER
    },
    questionId: {
      allowNull: false,
      type: DataTypes.INTEGER
    },
    text: {
      allowNull: false,
      type: DataTypes.STRING
    },
    nextQuestionId: {
      type: DataTypes.INTEGER
    }
  }, {
    classMethods: {
      associate: function(models) {
        models.QuestionOption.belongsTo(models.Question, {as: 'question', foreignKey: {field: 'questionId', allowNull: false}});
        models.QuestionOption.belongsTo(models.Question, {as: 'nextQuestion', foreignKey: {field: 'nextQuestionId', allowNull: true}});
      }
    }
  });
  return QuestionOption;
};

这些模型非常紧密地耦合在一起,并具有自引用连接和各种其他混乱。我觉得所有其他模型都与此无关,但如果需要,可以提供。

直接在 SQLite 数据库上执行 SQL,以与上面的 create 语句相同的顺序和相同的属性不会抛出异常,并且通过大量测试可以清楚地表明 Sequelize 不会尝试为 @ 运行 create 语句987654326@。它在生成要运行的 SQL 之前出错。

一些奇怪的行为是关联在模型中被精心定义,并且它们的定义中都有一个小写 qquestionId。所有关联还定义了反向关联,因此 Sequelize 不应尝试为属性创建名称。

在每次测试(成功)之前删除并重新创建所有表。

如果我从 QuestionOption 的 create 语句中删除 questionId: 1,那么错误就变成了

SequelizeValidationError: notNull Violation: questionId cannot be null, 
notNull Violation: QuestionId cannot be null

注意两者的外壳,一个在下(我去掉的那个),一个在上。

下一个嫌疑人是nextQuestionId关联,但它已在模型中定义,并且关联的每一侧为allowNull: true并且我已在create语句中提供了它。 p>

我完全对这种行为感到困惑,并质疑这是否可能是 sequelize 中的一个错误,尽管我需要在错误地报告它之前确认这一点。

其他可能有用的信息是:

  • 使用命令NODE_ENV=test mocha 运行测试
  • 使用 sync 自动为测试创建数据库(代码如下)
  • 所有其他测试均通过,但这是唯一在测试中使用 QuestionOption 的测试。
  • 我尝试测试的方法“在生产中”工作(在连接客户端的 dev 中本地运行)
  • 已使用 SQLite GUI 验证了数据库架构,并且所有列都是正确的(在任何带有大写 Q 的表中都没有 QuestionId 字段)

为测试创建数据库

beforeEach((done) => {
    Survey.DBModel.sync({force: true, logging: false}).then(() => {
        Question.DBModel.sync({force: true, logging: false}).then(() => {
            Answer.DBModel.sync({force: true, logging: false}).then(() => {
                QuestionOption.DBModel.sync({force: true, logging: false}).then(() => {
                    done();
                }, done);
            }, done);
        }, done);
    }, done);
});

【问题讨论】:

    标签: sequelize.js


    【解决方案1】:

    因此,在四处寻找答案并创建测试重现之后。事实证明,这是 Sequelize 的一个已知问题。解决方案,或者更糟糕的解决方法是添加明显的“缺失”属性,然后查询将起作用,它会丢弃分配给 ghost 属性的值。

    describe('answerQuestion', () => {
        it('should insert an answer and get the next question', (done) => {
            Survey.DBModel.create({lookType: 0}, {logging: false}).then(() => {
                Question.DBModel.create({type: 0, text: 'Test question'}, {logging: false}).then(q1 => {
                    Question.DBModel.create({type: 1, text: 'Next question'}, {logging: false}).then(q2 => {
                        QuestionOption.DBModel.create({
                            text: 'Test option', 
                            questionId: 1,
                            QuestionId: 1, <------ Workaround
                            nextQuestionId: 2}, {logging: false}).then(() => {
                            Survey.answerQuestion(1, 1, 1).then(question => {
                                question.should.have.property('id');
                            }, done);
                        }, done);
                    }, done);
                }, done);
            }, done);
        });
    });
    

    已知问题可以在here找到

    使用此问题测试重现here (如果更改将删除链接)

    希望这可以帮助其他人解决此问题。我确实希望这至少在他们的文档中被记录为一个已知问题......

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-07-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-11
      • 1970-01-01
      相关资源
      最近更新 更多