2

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

  1. 使用临时数据库在那里插入测试数据
  2. 每次我做测试时清除我所有的桌子

但是我正在寻找一种干净而前卫的方式,这样我就可以在没有手动清理的情况下使用我的数据库。有任何想法吗?

4

5 回答 5

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 执行安装/拆卸的命令行可执行文件。
  3. 可执行文件用于执行设置的 SQL 包文件。

因此,在每次测试之前,我们将运行它以根据源代码控制中的 SQL 包将数据库完全刷新到已知的初始状态。同样,这很慢,但它起作用了。可能有更快的解决方案对您有用。(也许不是删除并重新创建数据库,而是截断并重新填充表,或者删除并重新创建表等。修补它以找到适合您需要的内容。)

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

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

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

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

(首先测试整个域与内存中的模拟实现一起工作,然后用一个依赖实现再次测试该域,用另一个实现再次测试它,等等。我们最终每晚运行同一套测试十几次,持续十几次整个系统中不同的依赖实现。一次一个依赖。这让我们可以很容易地看到什么时候出了问题,因为在任何给定的测试运行中只有一个新变量。)

于 2013-01-04T13:34:07.353 回答
2

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

单元测试不同于集成测试。单元测试应该只针对感兴趣的类,而不是它的所有依赖项。

我假设您已经以事务方式对 DAO 进行了单元测试,因此无需证明它们再次正常工作。

您未提及的集成测试的另一个选项是使服务中的所有数据库交互都具有事务性。执行这些操作,然后在完成后回滚它们。

临时数据库是一个有吸引力的选择。您可以使用小而轻的东西,例如 Hypersonic、Derby 或 SQLLite。缺点是您必须更新您的模式两次:一次在测试数据库中,另一次在生产实例中。如果你无论如何都必须这样做,那就是洗头了。

于 2013-01-04T13:02:42.630 回答
2

如果您使用的是 Spring 框架,那么我认为您应该已经检查了 Spring关于如何进行测试的参考。

你会看到 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,请考虑事先更改您的表。

于 2013-01-04T13:37:12.967 回答
1

还有一个选项:事务回滚

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

于 2013-01-04T13:34:17.193 回答
1

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

于 2013-01-04T13:43:37.423 回答