我正在尝试为UI测试自动化做以下工作:
[SetUp]
public void TestSetUp()
{
_scope = new TransactionScope();
}
[TearDown]
public void TearDown()
{
_scope.Dispose();
}
[Test]
public void SomeTest()
{
Utilities.SomeDeleteTransaction(companyCode);
}
我正试图在[Test]中执行一个更新查询,并对UI做一些操作,并在测试后运行的[TearDown]滚同一事务,以便我的DB保持不变。我可以使用TransactionScope或其他类来实现这一点吗?
编辑
这个问题主要是为了处理硒测试的数据库已知状态。由于我的数据库每月都会从生产中删除,我希望能够在测试前执行一些insert/update/delete sql脚本来修改数据库,然后用Selenium进行一些UI测试,然后在Teardown中回滚(测试是用NUnit编写的),以确保数据库不会对测试产生任何影响,并且在测试后保持不变。
数据库快照!
保存此脚本并将其命名为"create_db_snapshot.sql"
/* Create a database snapshot */
USE master;
CREATE DATABASE Your_Database_Snapshot ON
(
NAME = Your_Database,
FILENAME = 'C:SnapshotsYour_Database_Snapshot.ss'
)
AS SNAPSHOT OF Your_Database;
GO
另外,保存此脚本并将其命名为"restore_db_from_snapshot.sql"
USE master;
RESTORE DATABASE Your_Database from
DATABASE_SNAPSHOT = 'Your_Database_Snapshot';
GO
示例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.Common;
using System.IO;
using System.Data.SqlClient;
[SetUp]
public void TestSetUp()
{
string sqlConnectionString = @"server=test.database.com;uid=your_db_username;pwd=your_db_password;database=Your_Database;";
string script = File.ReadAllText(@"~/create_db_snapshot.sql");
SqlConnection conn = new SqlConnection(sqlConnectionString);
Server server = new Server(new ServerConnection(conn));
server.ConnectionContext.ExecuteNonQuery(script);
}
[TearDown]
public void TearDown()
{
string sqlConnectionString = @"server=test.database.com;uid=your_db_username;pwd=your_db_password;database=Your_Database;";
string script = File.ReadAllText(@"~/restore_db_from_snapshot.sql");
SqlConnection conn = new SqlConnection(sqlConnectionString);
Server server = new Server(new ServerConnection(conn));
server.ConnectionContext.ExecuteNonQuery(script);
}
快照文档:https://msdn.microsoft.com/en-us/library/ms175158.aspx
执行.sql文件的代码信用:https://stackoverflow.com/a/1728859/3038677
在执行restore_db_from_snapshot.sql之前,您可能还需要运行此脚本
/* Kill all current connections to Your_Database */
use master;
DECLARE @kill varchar(8000) = '';
SELECT @kill = @kill + 'kill ' + CONVERT(varchar(5), spid) + ';'
FROM master..sysprocesses
WHERE dbid = db_id('Your_Database')
我使用数据库快照对web应用程序进行Selenium测试。在Setup方法中,我将数据库回滚到刚从生产中恢复数据库后拍摄的快照。这保证了数据库在每次测试运行时都处于相同的状态,但仍然允许您测试UI。我创建了几个临时存储过程,例如#usp_store_snapshot,以避免将数据库和单元测试与刚才用于测试的SQL代码一起丢弃。。
一种方法是使用https://msdn.microsoft.com/en-us/library/system.transactions.transactionscope.aspx它将自动回滚所有内容,即使在测试失败时也是如此,因为没有提交语句。
类似:
[Test]
public void SomeTest()
{
using (TransactionScope scope = new TransactionScope())
{
// here comes your test
}
}
一种"更好"one_answers"更安全"的方式是你在问题中告诉你的通过TearDown:的方式
[TestFixture]
public class YourFixture
{
private TransactionScope scope;
[SetUp]
public void TestSetUp()
{
scope = new TransactionScope();
}
[TearDown]
public void TearDown()
{
scope.Dispose();
}
[Test]
public void SomeTest()
{
// here comes your test
}
}
为什么?因为NUnit是你的garanty,所以TearDown会被调用。
如果您可以保证数据库中只有一个线程,那么TearDown就可以恢复该数据库的已知良好备份。我想,对于非常大的数据库来说,这可能会很麻烦。
我们采用了两种方法之一:
- 拆卸时分离/重新附加原始数据库。。。这听起来确实很贵,但如果有很多东西可以回滚,它可能会更便宜。确实需要一些管道来保存原始数据库文件的副本,并四处复制,等等
- 在安装程序中启动DTC事务并在拆解时处理。。。如果测试设置相对较轻,则可以正常工作
为了让#1更快,我们用RAM磁盘进行了实验,使磁盘拷贝速度闪电般快。如果你走那条路,这会产生巨大的不同。DTC事务范围是最自然的方法,但如果你在设置中创建1000条记录,然后回滚,你的测试可能会变得很慢,这不是一件好事。
附加/分离的一些示例代码:
public void Reset()
{
if (!this.initialized || !this.connectionString.Contains("(local)"))
return;
TestDbManager.CopyNewFiles(this.remoteDatabaseSourceFolder, this.localDatabaseFilesCacheFolder);
this.Detach(this.database);
TestDbManager.CopyNewFiles(this.localDatabaseFilesCacheFolder, this.localSqlServerWorkingFolder);
this.ReAttach(this.database, this.localSqlServerWorkingFolder);
}
所以您必须跟踪(a)原始数据库文件和(b)活动数据库文件的文件夹。每次分离测试后,从a复制到b,然后附加。
附加/分离是用简单的命令完成的。。。
exec sp_attach_db @dbname = '{0}'"
exec sp_detach_db @dbname = '{0}'"
由于我们使用多个数据库来封装所有这些,因此我们有一个小的助手类。
拥有非常大或长时间运行的事务可能会隐藏或创建错误,并可能导致其他不必要的副作用。
如前所述,SNAPSHOT(我投了赞成票:)
在开始时创建快照,在结束时返回
或者。。。你能在数据库发生变化时创建一个快照吗?例如,当你需要的时候,然后继续恢复。在这种情况下,快照就像一个非常轻量级的备份。
CREATE DATABASE database_snapshot_name
ON
(
NAME = logical_file_name,
FILENAME = 'os_file_name'
) [ ,...n ]
AS SNAPSHOT OF source_database_name
恢复到SNAPSHOT
USE master;
-- Reverting AdventureWorks to AdventureWorks_dbss1800
RESTORE DATABASE AdventureWorks from
DATABASE_SNAPSHOT = 'AdventureWorks_dbss1800';
GO
两者都来自MSDN
https://msdn.microsoft.com/en-us/library/ms189281.aspx
https://msdn.microsoft.com/en-us/library/ms175876.aspx
我不知道你是被C#代码还是SQL代码打动了。有一种方法可以在SQL中实现。开始事务,更新数据,读取未提交的数据,然后回滚。
Begin Tran
Update table1
set col1=val2
where col1=val1
select col1 from table1 WITH (NOLOCK);
rollback