SQLite单元测试NHibernate生成的级联关系

时间:2009-08-04 14:44:00

标签: unit-testing nhibernate sqlite

以下是本文的一些背景信息。如果你愿意,你可以跳过这个问题:

在这篇优秀的文章(http://ayende.com/Blog/archive/2009/04/28/nhibernate-unit-testing.aspx)中,作者认为“当使用NHibernate时,我们通常只想测试三件事:
1)属性是持久的,
2)级联按预期工作 3)查询返回正确的结果。 - )映射完成&正确的(隐含的)

我认为他继续说SQLite可以而且应该是完成上述所有选择的单元测试工具。应该指出的是,作者似乎是那里经验丰富,技术娴熟的NHib开发人员之一,虽然他在文章中没有明确说明,但他后来在一个问题中暗示该域名可以而且应该处理一些SQLite的缺点。

问题:

如何使用SQLite测试级联关系,特别是考虑到它不检查外键约束。如何测试模型以确保外键约束不会成为db问题。

以下是我为测试级联行为而提出的一些单元测试。该模型只是一个部门,可以有零到多个StaffMembers,级联设置为NONE。

    [Test]
    public void CascadeSaveIsNone_NewDepartmentWithFetchedStaff_CanSaveDepartment()
    {
        _newDept.AddStaff(_fetchedStaff);
        Assert.That(_newDept.IsTransient(), Is.True);

        _reposDept.SaveOrUpdate(_newDept);
        _reposDept.DbContext.CommitChanges();

        Assert.That(_newDept.IsTransient(), Is.False);
    }

    [Test]
    public void CascadeSaveIsNone_NewDepartmentWithFetchedStaff_CannotSaveNewStaff()
    {
        _newDept.AddStaff(_newStaff);
        Assert.That(_newDept.IsTransient(), Is.True);
        Assert.That(_newStaff.IsTransient(), Is.True);

        _reposDept.SaveOrUpdate(_newDept);
        _reposDept.DbContext.CommitChanges();

        Assert.That(_newDept.IsTransient(), Is.False);
        Assert.That(_newStaff.IsTransient(), Is.True);
    }

    [Test]
    public void CascadeDeleteIsNone_FetchedDepartmentWithFetchedStaff_Error()
    {
        _fetchedDept.AddStaff(_fetchedStaff);
        _reposDept.SaveOrUpdate(_fetchedDept);
        _reposStaff.DbContext.CommitChanges();

        _reposDept.Delete(_fetchedDept);
        var ex = Assert.Throws<GenericADOException>(() => _reposDept.DbContext.CommitChanges());

        Console.WriteLine(ex.Message);
        Assert.That(ex.Message, Text.Contains("could not delete:"));
        Console.WriteLine(ex.InnerException.Message);
        Assert.That(ex.InnerException.Message, Text.Contains("The DELETE statement conflicted with the REFERENCE constraint"));
    }

    [Test]
    public void Nullable_NewDepartmentWithNoStaff_CanSaveDepartment()
    {
        Assert.That(_newDept.Staff.Count(), Is.EqualTo(0));

        var fetched = _reposDept.SaveOrUpdate(_newDept);
        Assert.That(fetched.IsTransient(), Is.EqualTo(false));
        Assert.That(fetched.Staff.Count(), Is.EqualTo(0));
    }

第三个测试“.._ FetchedDepartmentWithFetchedStaff_Error”对Sql Server起作用,但对SQLite不起作用,因为后者不检查外键约束。

以下是关系另一方面的测试;一个StaffMember可以有一个部门,级联设置为NONE。

    [Test]
    public void CascadeSaveIsNone_NewStaffWithFetchedDepartment_CanSaveStaff()
    {
        _newStaff.Department = _fetchedDept;
        _reposStaff.SaveOrUpdate(_newStaff);
        _reposStaff.DbContext.CommitChanges();

        Assert.That(_newStaff.Id, Is.GreaterThan(0));
    }

    [Test]
    public void CascadeSaveIsNone_NewStaffWithNewDepartment_Error()
    {
        _newStaff.Department = _newDept;
        Assert.That(_newStaff.IsTransient(), Is.True);

        var ex = Assert.Throws<PropertyValueException>(() => _reposStaff.SaveOrUpdate(_newStaff));
        Console.WriteLine(ex.Message);
        Assert.That(ex.Message, Text.Contains("not-null property references a null or transient value"));
    }

    [Test]
    public void CascadeDeleteIsNone_FetchedStaffWithFetchedDepartment_DeletesTheStaff_DoesNotDeleteTheDepartment()
    {
        _newStaff.Department = _fetchedDept;
        _reposStaff.SaveOrUpdate(_newStaff);
        _reposStaff.DbContext.CommitChanges();

        _reposStaff.Delete(_newStaff);
        Assert.That(_reposStaff.Get(_newStaff.Id), Is.Null);
        Assert.That(_reposDept.Get(_fetchedDept.Id), Is.EqualTo(_fetchedDept));
    }

    [Test]
    public void NotNullable_NewStaffWithUnknownDepartment_Error()
    {
        var noDept = new Department("no department");
        _newStaff.Department = noDept;

        var ex = Assert.Throws<PropertyValueException>(() => _reposStaff.SaveOrUpdate(_newStaff));
        Console.WriteLine(ex.Message);
        Assert.That(ex.Message, Text.Contains("not-null property references a null or transient"));
    }

    [Test]
    public void NotNullable_NewStaffWithNullDepartment_Error()
    {
        var noDept = new Department("no department");
        _newStaff.Department = noDept;

        var ex = Assert.Throws<PropertyValueException>(() => _reposStaff.SaveOrUpdate(_newStaff));
        Console.WriteLine(ex.Message);
        Assert.That(ex.Message, Text.Contains("not-null property references a null or transient"));
    }

这些测试是针对Sql Server和SQLite进行的。我可以信任SQLite测试吗?这些值得测试吗?

干杯,
Berryl

1 个答案:

答案 0 :(得分:3)

据我所知,这篇文章是关于测试NHibernate Mapping的。在我看来,这与db相关的问题无关,而是测试你在映射中设置的nhibernate属性。没有必要断言无法创建无效数据:您只需要证明您的代码创建了所需的结果和/或检查您要检查的内容。您可以测试级联,级联删除和删除孤立。在使用sqlite的测试中,无论你想要什么样的方式。但是第三次​​测试试图测试约束,这并不是nhibernate所担心的。

如果你想测试你的Db约束,你应该确实使用你的生产数据库,而不是sqlite。您可以使用或不使用hibernate来执行此操作,但这与您的映射无关。 另一方面,如果您真的想要使用SQLite进行外键测试,那么您可以尝试使用此foreign_key_trigger_generator。我没有尝试,但它似乎生成before-insert-triggers,以确保引用的Pk的存在。 也许你可以写这个工具有用的评论。