用Sqlite的内存数据库对nhibernate进行单元测试
针对数据访问代码的单元测试处在一个尴尬的位置上,如果操作不是针对真实的数据库执行的,就无法捕获数据库特定的错误,比如 sql 语句语法是否正确,操作是否违反了数据库约束,事务是否正确提交。并且,测试之间应该是隔离的,一个测试不能影响另一个测试的数据,就是说,每个测试运行之前都要重建表结构,重新安装测试数据。在一个真实的数据库上执行这些操作会让测试成为老牛破车。
所幸的是Sqlite提供了内存数据库,避免磁盘IO可以带来性能提升。内存数据库有一个非常重要的特点:即数据库仅在连接打开的时候存在,一旦连接关闭,数据库随即消失。这正是我们想要的,运行测试的步骤如下:
1,在 [TestInitialize] 方法中打开 Session,创建表结构,安装测试数据。默认情况下,Sqlite的Unicode字符串比较是区分大小写的,所以,创建表结构的时候要为 TEXT 列指定 COLLATE NOCASE。
2,运行测试
3,在 [TestCleanup] 方法中关闭 Session,这将导致底层的连接关闭,内存数据库消失。
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
[TestClass] public class MyTest
{ [AssemblyInitialize]
public static void InitAssembly(TestContext testContext)
{
NHibernateHelper.Init();
}
[AssemblyCleanup]
public static void CleanupAssembly()
{
NHibernateHelper.CloseSessionFactory();
}
protected ISession Session { get ; private set ; }
[TestInitialize()]
public void MyTestInitialize()
{
try
{
// 1 打开 Session
Session = NHibernateHelper.OpenSession();
// 2 创建表
NHibernate.Tool.hbm2ddl.SchemaExport export = new NHibernate.Tool.hbm2ddl.SchemaExport(NHibernateHelper.Configuration);
export.SetDelimiter( ";" );
StringWriter sw = new StringWriter();
export.Execute( false , false , false , Session.Connection, sw);
using (IDbCommand cmd = Session.Connection.CreateCommand())
{
// 替换字段定义语句
cmd.CommandText = Regex.Replace(sw.ToString(), @"\s+TEXT\s+" , " TEXT COLLATE NOCASE " , RegexOptions.IgnoreCase | RegexOptions.Compiled);
cmd.ExecuteNonQuery();
}
// 3 创建测试数据
using (ITransaction tx = Session.BeginTransaction())
{
Role role = new Role();
role.Name = "admins" ;
Session.Save(role);
tx.Commit();
}
// 4 清除 Session 缓存
Session.Clear();
}
catch (Exception ex)
{
// 如果发生异常,则 TestCleanup 不会执行,因此在这里回收资源
if (Session != null )
{
Session.Close();
}
throw ;
}
}
[TestCleanup()]
public void MyTestCleanup()
{
Session.Close();
Session = null ;
}
[TestMethod]
public void MyTestMethod()
{
using (ITransaction tx = Session.BeginTransaction())
{
Role role = Session.Query<Role>().FirstOrDefault(x => x.Name == "admins" );
Assert.IsNotNull(role);
tx.Commit();
}
}
} |
NHibernate 配置,注意 connection.release_mode 属性一定要设为 on_close
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
< hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
< bytecode-provider type="null"/>
< reflection-optimizer use="false"/>
< session-factory >
< property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</ property >
< property name="connection.driver_class">NHibernate.Driver.SQLite20Driver</ property >
< property name="connection.connection_string">
data source=:memory:
</ property >
< property name="dialect">NHibernate.Dialect.SQLiteDialect</ property >
< property name="connection.release_mode">on_close</ property >
< property name="hbm2ddl.keywords">none</ property >
< property name="current_session_context_class">managed_web</ property >
< property name="show_sql">false</ property >
< property name="query.substitutions">true 1, false 0, yes ‘Y‘, no ‘N‘</ property >
< property name="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</ property >
< mapping assembly="WuTong"/>
</ session-factory >
</ hibernate-configuration >
|
参考链接:
http://ayende.com/blog/3983/nhibernate-unit-testing
http://www.sqlite.org/faq.html#q18