如何:编写在单个事务范围内运行的数据库单元测试

如何:编写在单个事务范围内运行的数据库单元测试,第1张

可以修改单元测试,使其在单个事务范围内运行。这样您便可以在测试结束之后回滚在测试过程中执行的任何更改。下面的过程说明了具体的做法:在使用 BEGIN TRANSACTION 和ROLLBACK TRANSACTION 的T-SQL 测试脚本中创建事务。为某个测试类中的单个测试方法创建一个事务。为给定测试类中的所有测试方法创建一个事务。使用T-SQL 创建事务在数据库单元测试设计器中打开单元测试。指定要创建事务的脚本类型。例如,可以指定预先测试、测试或后期测试。在T-SQL 编辑器中输入测试脚本。插入BEGIN TRANSACTION 和ROLLBACK TRANSACTION 语句,如下面的简单示例所示。此示例使用包含 50 行数据的 Northwind 数据库表: BEGIN TRANSACTION TestTransaction UPDATE "Order Details" set Quantity = Quantity + 10 IF @@ROWCOUNT!=50 RAISERROR('Row count does not equal 50',16,1) ROLLBACK TRANSACTION TestTransaction 注意在执行 COMMIT TRANSACTION 语句之后不能对事务进行回滚。有关ROLLBACK TRANSACTION 如何与存储过程和触发器一起工作的更多信息,请参见ROLLBACK TRANSACTION (Transact-SQL)。为单个测试方法创建事务打开单元测试的 Visual Basic 或 C# 文件。包装预先测试、测试和后期测试 *** 作,如下面的 Visual Basic 代码示例所示: _ Public Sub dbo_InsertTable1Test() Dim tx As New CommittableTransaction ExecutionContext.Connection.EnlistTransaction(tx) PrivilegedContext.Connection.EnlistTransaction(tx) Dim testActions As DatabaseTestActions = Me.dbo_InsertTable1TestData 'Execute the pre-test script ' System.Diagnostics.Trace.WriteLineIf((Not (testActions.PretestAction) Is Nothing), "Executing pre-test script...") Dim pretestResults() As ExecutionResult = TestService.Execute(Me.PrivilegedContext, Me.PrivilegedContext, testActions.PretestAction) 'Execute the test script System.Diagnostics.Trace.WriteLineIf((Not (testActions.TestAction) Is Nothing), "Executing test script...") Dim testResults() As ExecutionResult = TestService.Execute(ExecutionContext, Me.PrivilegedContext, testActions.TestAction) 'Execute the post-test script ' System.Diagnostics.Trace.WriteLineIf((Not (testActions.PosttestAction) Is Nothing), "Executing post-test script...") Dim posttestResults() As ExecutionResult = TestService.Execute(Me.PrivilegedContext, Me.PrivilegedContext, testActions.PosttestAction) tx.Rollback() End Sub Private dbo_InsertTable1TestData As DatabaseTestActions 注意如果您使用的是 Visual Basic,那么,除了添加 Imports Microsoft.VisualStudio.TestTools.UnitTesting、Imports Microsoft.VisualStudio.TeamSystem.Data.UnitTesting 和Imports Microsoft.VisualStudio.TeamSystem.Data.UnitTest.Conditions 以外,还必须添加 Imports System.Transactions。如果您使用的是 Visual C#,那么,除了为 Microsoft.VisualStudio.TestTools、Microsoft.VisualStudio.TeamSystem.Data.UnitTesting 和 Microsoft.VisualStudio.TeamSystem.Data.UnitTesting.Conditions 添加 using 语句外,还必须添加 using System.Transactions。还必须在这些程序集中添加对项目的引用。在TestInitialize 中创建事务,并在 TestCleanup 中释放它,如下面的 Visual C# 代码示例所示: TransactionScope _trans[TestInitialize()] public void Init() { _trans = new TransactionScope()base.InitializeTest()} [TestCleanup()] public void Cleanup() { base.CleanupTest()_trans.Dispose()} [TestMethod()] public void TransactedTest() { DatabaseTestActions testActions = this.DatabaseTestMethod1Data// Execute the pre-test script // System.Diagnostics.Trace.WriteLineIf((testActions.PretestAction != null), "Executing pre-test script...")ExecutionResult[] pretestResults = TestService.Execute(this.PrivilegedContext, this.PrivilegedContext, testActions.PretestAction)// Execute the test script // System.Diagnostics.Trace.WriteLineIf((testActions.TestAction != null), "Executing test script...")ExecutionResult[] testResults = TestService.Execute(this.ExecutionContext, this.PrivilegedContext, testActions.TestAction)// Execute the post-test script // System.Diagnostics.Trace.WriteLineIf((testActions.PosttestAction != null), "Executing post-test script...")ExecutionResult[] posttestResults = TestService.Execute(this.PrivilegedContext, this.PrivilegedContext, testActions.PosttestAction)}

关于junit单元测试工具的安装请参看第二课的内容(其实就是导入一个junit的jar包就行了)

首先认识几个注解标签

@Test:测试方法

@Before:初始化方法

@After:释放资源

执行顺序:@Before->@Test->@After

第一步新建测试文件夹(目的就是用来存放测试类,使项目更整洁,分类明确,好管理)

选中项目右键new->Source Folder 输入文件夹的名称例如test

在测试文件夹下创建测试类(就是创建个普通的类)

如下在测试类中使用junit进行单元测试

下面只是先搭建一个测试框架

搭建好之后测试hibernate访问数据库的代码注意:导入的包不要弄错,都是hibernate的包

public class StudentTest { private SessionFactory sessionFactory private Session session private Transaction transaction @Before public void init(){ //创建配置对象 Configuration config = new Configuration() //创建服务注册对象 ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(config.getProperties()).buildServiceRegistry() //创建会话工厂对象 sessionFactory = config.buildSessionFactory(serviceRegistry) //会话对象 session = sessionFactory.openSession() //开启事务 transaction = session.beginTransaction() } @Test public void testSaveStudents(){ Student s = new Student(1, "小明", new Date(), "北京xxxx街道5号楼201") session.save(s)//保存对象到数据库 } @After public void destory(){ transaction.commit()//提交事务 session.close()//关闭会话 sessionFactory.close()//关闭会话工厂 }}

进行测试如下: 选中测试方法右键run as ->junit test就行了

执行成功控制台打印信息:

查看数据库表

可以看到增加了一条信息,测试成功

单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。

比如对函数abs(),我们可以编写出以下几个测试用例:

输入正数,比如1、1.2、0.99,期待返回值与输入相同;

输入负数,比如-1、-1.2、-0.99,期待返回值与输入相反;

输入0,期待返回0;

输入非数值类型,比如None、[]、{},期待抛出TypeError。

把上面的测试用例放到一个测试模块里,就是一个完整的单元测试。

如果单元测试通过,说明我们测试的这个函数能够正常工作。如果单元测试不通过,要么函数有bug,要么测试条件输入不正确,总之,需要修复使单元测试能够通过。

单元测试通过后有什么意义呢?如果我们对abs()函数代码做了修改,只需要再跑一遍单元测试,如果通过,说明我们的修改不会对abs()函数原有的行为造成影响,如果测试不通过,说明我们的修改与原有行为不一致,要么修改代码,要么修改测试。

这种以测试为驱动的开发模式最大的好处就是确保一个程序模块的行为符合我们设计的测试用例。在将来修改的时候,可以极大程度地保证该模块行为仍然是正确的。

我们来编写一个Dict类,这个类的行为和dict一致,但是可以通过属性来访问,用起来就像下面这样:

>>>d = Dict(a=1, b=2)

>>>d['a']

1

>>>d.a

1

mydict.py代码如下:

class Dict(dict):

def __init__(self, **kw):

super(Dict, self).__init__(**kw)

def __getattr__(self, key):

try:

return self[key]

except KeyError:

raise AttributeError(r"'Dict' object has no attribute '%s'" % key)

def __setattr__(self, key, value):

self[key] = value

为了编写单元测试,我们需要引入Python自带的unittest模块,编写mydict_test.py如下:

import unittest

from mydict import Dict

class TestDict(unittest.TestCase):

def test_init(self):

d = Dict(a=1, b='test')

self.assertEquals(d.a, 1)

self.assertEquals(d.b, 'test')

self.assertTrue(isinstance(d, dict))

def test_key(self):

d = Dict()

d['key'] = 'value'

self.assertEquals(d.key, 'value')

def test_attr(self):

d = Dict()

d.key = 'value'

self.assertTrue('key' in d)

self.assertEquals(d['key'], 'value')

def test_keyerror(self):

d = Dict()

with self.assertRaises(KeyError):

value = d['empty']

def test_attrerror(self):

d = Dict()

with self.assertRaises(AttributeError):

value = d.empty

编写单元测试时,我们需要编写一个测试类,从unittest.TestCase继承。

以test开头的方法就是测试方法,不以test开头的方法不被认为是测试方法,测试的时候不会被执行。

对每一类测试都需要编写一个test_xxx()方法。由于unittest.TestCase提供了很多内置的条件判断,我们只需要调用这些方法就可以断言输出是否是我们所期望的。最常用的断言就是assertEquals():

self.assertEquals(abs(-1), 1) # 断言函数返回的结果与1相等

另一种重要的断言就是期待抛出指定类型的Error,比如通过d['empty']访问不存在的key时,断言会抛出KeyError:

with self.assertRaises(KeyError):

value = d['empty']

而通过d.empty访问不存在的key时,我们期待抛出AttributeError:

with self.assertRaises(AttributeError):

value = d.empty

运行单元测试

一旦编写好单元测试,我们就可以运行单元测试。最简单的运行方式是在mydict_test.py的最后加上两行代码:

if __name__ == '__main__':

unittest.main()

这样就可以把mydict_test.py当做正常的python脚本运行:

$ python mydict_test.py

另一种更常见的方法是在命令行通过参数-m unittest直接运行单元测试:

$ python -m unittest mydict_test

.....

----------------------------------------------------------------------

Ran 5 tests in 0.000s

OK

这是推荐的做法,因为这样可以一次批量运行很多单元测试,并且,有很多工具可以自动来运行这些单元测试。

setUp与tearDown

可以在单元测试中编写两个特殊的setUp()和tearDown()方法。这两个方法会分别在每调用一个测试方法的前后分别被执行。

setUp()和tearDown()方法有什么用呢?设想你的测试需要启动一个数据库,这时,就可以在setUp()方法中连接数据库,在tearDown()方法中关闭数据库,这样,不必在每个测试方法中重复相同的代码:

class TestDict(unittest.TestCase):

def setUp(self):

print 'setUp...'

def tearDown(self):

print 'tearDown...'

可以再次运行测试看看每个测试方法调用前后是否会打印出setUp...和tearDown...。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/sjk/9907697.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-03
下一篇 2023-05-03

发表评论

登录后才能评论

评论列表(0条)

保存