如何在不影响数据库的情况下测试我的服务?

时间:2013-01-04 12:59:58

标签: database spring testing rollback

我正在为Spring中的应用程序编写一些集成测试。我想测试一些服务,但是他们可能会调用一些数据访问对象,新数据将保存在我的数据库中。我有两种方法可以清除数据库中的所有内容测试完成后:

  1. 使用临时数据库在那里插入测试数据
  2. 每次进行测试时清除所有表格
  3. 然而,我正在寻找让我们说干净且前进的方式,以便我可以使用我的数据库而无需手动清理。有任何想法吗?

5 个答案:

答案 0 :(得分:3)

回答有关问题的评论并给出答案,因为仅凭评论无法捕捉到它......

在每次测试之间设置和拆除数据库对于性能来说肯定是非常昂贵的。你如何去做它完全取决于数据库本身。 (MS SQL,MySQL,PostgreSQL等)如果您可以在使用相同代码的情况下更换完全内存数据库,那么这将是每次测试设置和拆除时最快的数据。

作为一个例子,我曾经有一个项目,我需要针对MS SQL实例运行我的测试。设置和拆解使测试减慢了批次,但我们都认为扩展测试覆盖率是值得的。 (我们每晚都进行集成测试,而不是在办理登机手续时,所以无论如何这都不是什么大问题。)为了完全刷新数据库,我们使用了这段代码(C#,对不起):

var procInfo = new ProcessStartInfo
{
    Arguments = string.Format(@"/Action:Publish /SourceFile:{0} /p:CreateNewDatabase=True /TargetConnectionString:""{1}""", ConfigurationManager.AppSettings["SQLPackageFile"], ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString),
    CreateNoWindow = true,
    ErrorDialog = false,
    FileName = ConfigurationManager.AppSettings["SQLPackageExecutable"],
    RedirectStandardOutput = true,
    UseShellExecute = false
};
var proc = new Process
{
    StartInfo = procInfo
};
proc.Start();

然后在测试配置中我们有三个值:

  1. 数据库的连接字符串。
  2. MS SQL的命令行可执行文件,用于执行setup / teardown。
  3. 可执行文件用于执行安装的SQL包文件。
  4. 因此,在每次测试之前,我们将运行此操作,以根据源代码管理中的SQL包将数据库完全刷新到已知的初始状态。再次,这是非常慢但它起作用。可能有更快的解决方案适合您。 (也许不是删除和重新创建数据库,而是截断并重新填充表,或者删除并重新创建表等。修改它以找到适合您需求的内容。)

    此外,我们希望确保这适合我们的域模型,而不会污染SQL依赖项之外的任何内容。我们用于数据访问的模型是一个存储库模式。基本上我们的中央域代码具有域模型(不与DB表或任何其他依赖项耦合)和这些模型的存储库接口。然后,数据访问项目实现了这些接口,并在域模型和数据库表/列之间进行内部映射。

    (我们还有两个针对相同接口的其他存储库实现项目。一个是用于域单元测试的内存实现。它只是保存发送到存储库的模型的静态列表。另一个是使用的实现每个数据持久性的XML文件,用于在没有SQL实例的情况下运行软件。应用程序的任何给定实例使用的是由依赖注入容器的配置设置确定的。)

    所以我们所做的是在名为DataResetter的域中添加另一个接口。然后每个数据访问实现(SQL,内存中,XML)实现该接口。 SQL使用上面的代码和配置值,内存中的一个只清除了它的静态列表,而XML只删除了XML文件。

    这允许测试使用域功能进行测试设置和拆卸,并允许依赖注入容器确定要使用的实现。这样,测试就没有与任何特定的实现相结合。另外一个好处是,相同的测试可用于单元和集成目的。它们之间的区别只不过是配置设置。

    (首先测试完整域是否使用内存模拟实现,然后使用一个依赖项实现再次测试域,再次使用另一个依赖项测试等等。我们最终每晚运行相同的测试套件十几次在整个系统中有十几个不同的依赖实现。一次一个依赖。这使我们可以很容易地看到什么时候被破坏,因为在任何给定的测试运行中只有一个新的变量。)

答案 1 :(得分:2)

还有另一种选择:在测试服务时为DAO使用模拟。

单元测试与集成测试不同。单元测试应仅关注感兴趣的类,而不是所有依赖项。

我假设您已经以交易方式对DAO进行了单元测试,因此无需证明它们能够再次正常运行。

您未提及的集成测试的另一个选项是从事务服务中进行所有数据库交互。完成操作,然后在完成后回滚。

临时数据库是一个很有吸引力的选择。你可以使用像Hypersonic或Derby或SQLLite这样小而轻的东西。缺点是您必须两次更新模式:一次在测试数据库中,一次又在生产实例中。如果你不得不这样做,这是一个洗涤。

答案 2 :(得分:2)

如果您正在使用Spring框架,那么我认为您应该已经检查了Spring reference如何进行测试。

你会发现Spring会让你变得如此简单,以一种你不需要直接与事务交互的方式。在测试的顶部添加一个 @Transactionl 注释就是你所需要的要做,那么进行测试事务有什么好处?是!如前面提到的其他答案,它将允许您回滚您的交易,因此您的数据库中不会留下任何内容。看一下这个示例代码:

    @Transactional
    public class FictitiousTransactionalTest {

        @Before
        public void setUpTestDataWithinTransaction() {
            // set up test data within the transaction
        }

        @Test
        @Rollback(true)
        public void modifyDatabaseWithinTransaction() {
            // logic which uses the test data and modifies database state
        }
    }

需要注意的重要一点是你的数据库必须是InnoDB,所以如果你使用默认为myISAM的mySQL,请考虑事先改变你的表。

答案 3 :(得分:1)

还有另一个选项:交易回滚

根据您打开和提交交易的方式,可能不会提交您的交易,而是可以返回一个角色。

答案 4 :(得分:1)

只需在测试方法上添加@Transactional注释,spring就会自动执行回滚。