没有本地数据库的TDD?

时间:2017-06-02 03:33:45

标签: java ruby-on-rails database unit-testing tdd

当我们开发Rails应用程序时,我们在开发环境中使用本地数据库,并确保我们的规范作为TDD的一部分传递。

在Java中使用TDD时,不使用类似于Sqlite的本地数据库是一种规范吗?我被告知内存数据库(HSQL)是运行单元和集成测试所需的全部内容。这是遵循的标准做法吗?

我们在Rails应用程序中使用Sqlite进行本地开发和运行Rspecs。但我的问题是Java开发。我们正在努力用Java重写我们的应用程序的一部分。有人告诉我,如果编写涵盖所有功能的集成测试,则不需要任何数据库进行开发。并且已经被告知HSQL就足够了。因为我习惯在Rails中使用数据库进行本地开发,所以我想知道你以后如何调试任何问题?如果我们可以在本地环境中复制数据和场景,那么分析任何问题都非常有用。如果您不在开发环境中使用任何数据库并完全依赖HSQL进行测试,那么如何在Java / Spring中执行相同的操作?

3 个答案:

答案 0 :(得分:0)

您的问题的答案:让您的测试环境数据库尽可能接近开发环境是非常普遍的。

我认为你全神贯注于性能,在考虑使用内存数据库之前,还有一些关键的事情可以改进。

通常在进行TDD时,您只会运行所涉及的测试,然后运行整个套件以检查您是否没有破坏任何内容。如果您使用的是Rspec,则可以使用tags

另一个重要的事情是在每次测试开始时清理数据库,因为测试应该被隔离,并且永远不依赖于先前测试的结果。这将改进您系统中可能存在的复杂搜索查询。有一个gem可以帮到你。

最后,如果您使用某种持续集成工具,请记住使用rake db:schema:load而不是rake db:migrate进行设置。这将作为单个迁移运行您的模式文件,而不是每次提交时运行每个单独的迁移。 (请记住保持此版本控制并始终保持最新状态)

答案 1 :(得分:0)

你的术语错误了。 TDD是关于编写测试用例的。但大部分时间以及在您的问题中,人们都会考虑将TDD用于单元测试

不幸的是,条款不是很清楚。当你转向wikipedia时,你会发现(我的话):“你为测试一个软件做的任何事情都可以称为单元测试。”

但这没有用。您应该寻找here等定义。那里的主要方面是:单元测试是孤立的。引用该链接:

  

在内存中运行(例如,无数据库或文件访问)

因此:

  • 进行单元测试时,应该不使用任何数据库
  • 在集成测试时,您希望确保您的解决方案“端到端”工作。从这个意义上说,您可能正在使用数据库的特殊实例,但不是使用不同的数据库。

答案 2 :(得分:0)

对我来说,我从不使用任何数据库,包括 HSQLDB 来编写单元测试。

我更喜欢创建一些 Schema schema = new Schema.Builder() .addColumnInteger("age") .addColumnCategorical("workclass", "Private", "Self-emp-not-inc", "Self-emp-inc", "Federal-gov", "Local-gov", "State-gov", "Without-pay", "Never-worked") .addColumnInteger("fnlwgt") .addColumnCategorical("education", "Bachelors", "Some-college", "11th", "HS-grad", "Prof-school", "Assoc-acdm", "Assoc-voc", "9th", "7th-8th", "12th", "Masters", "1st-4th", "10th", "Doctorate", "5th-6th", "Preschool") .addColumnInteger("education-num") .addColumnCategorical("marital-status", "Married-civ-spouse", "Divorced", "Never-married", "Separated", "Widowed", "Married-spouse-absent", "Married-AF-spouse") .addColumnCategorical("occupation", "Tech-support", "Craft-repair", "Other-service", "Sales", "Exec-managerial", "Prof-specialty", "Handlers-cleaners", "Machine-op-inspct", "Adm-clerical", "Farming-fishing", "Transport-moving", "Priv-house-serv", "Protective-serv", "Armed-Forces") .addColumnCategorical("relationship", "Wife", "Own-child", "Husband", "Not-in-family", "Other-relative", "Unmarried") .addColumnCategorical("race", "White", "Asian-Pac-Islander", "Amer-Indian-Eskimo", "Other", "Black") .addColumnCategorical("sex", "Female", "Male") .addColumnInteger("capital-gain") .addColumnInteger("capital-loss") .addColumnInteger("hours-per-week") .addColumnCategorical("native-country", "United-States", "Cambodia", "England", "Puerto-Rico", "Canada", "Germany", "Outlying-US(Guam-USVI-etc)", "India", "Japan", "Greece", "South", "China", "Cuba", "Iran", "Honduras", "Philippines", "Italy", "Poland", "Jamaica", "Vietnam", "Mexico", "Portugal", "Ireland", "France", "Dominican-Republic", "Laos", "Ecuador", "Taiwan", "Haiti", "Columbia", "Hungary", "Guatemala", "Nicaragua", "Scotland", "Thailand", "Yugoslavia", "El-Salvador", "Trinadad&Tobago", "Peru", "Hong", "Holand-Netherlands") .addColumnCategorical("class", ">50K", "<=50K") .build(); TransformProcess tp = new TransformProcess.Builder(schema) .transform(new CategoricalToOneHotTransform("workclass")) .transform(new CategoricalToOneHotTransform("education")) .transform(new CategoricalToOneHotTransform("marital-status")) .transform(new CategoricalToOneHotTransform("occupation")) .transform(new CategoricalToOneHotTransform("relationship")) .transform(new CategoricalToOneHotTransform("race")) .transform(new CategoricalToOneHotTransform("sex")) .transform(new CategoricalToOneHotTransform("native-country")) .transform(new CategoricalToIntegerTransform("class")) .build(); Schema outputSchema = tp.getFinalSchema(); int numLinesToSkip = 0; String delimiter = ","; CSVRecordReader recordReader = new CSVRecordReader(numLinesToSkip, delimiter); recordReader.initialize(new FileSplit(Paths.get("..\\adult.data").toFile())); int labelIndex = outputSchema.getColumnNames().size() - 1; int numClasses = 2; int batchSize = 2000; RecordReaderDataSetIterator iterator = new RecordReaderDataSetIterator(recordReader, batchSize, labelIndex, numClasses); DataSet allData = iterator.next(); allData.shuffle(); SplitTestAndTrain testAndTrain = allData.splitTestAndTrain(0.65); ,如:interface。并让SUT与之通信。然后我写了一些实现类让它们实现我创建的接口。类层次结构如下所示:

*Repository

这种方法称为Separation of Concerns。应用程序域是一个问题,数据访问是另一个问题。遵循此方法会产生许多插件兼容组件和独立模块,例如: <<uses>> SUT ---------------> Repository ^ | <<implement>> | |--------|--------|-------| | | | | JPA Hibernate JDBC .etc domainjpa和.etc,但重要的是让你的测试更加可测试

然后我使用Test Double来模拟/删除它在单元测试中的协作,测试它们是按预期一起工作的。伪代码如下:

jdbc

但是您必须使用数据库编写一些集成测试来测试在第三方API上运行的每个repo = mock(Repository.class); SUT it = new SUT(repository); when(repo.find(id)).thenReturn(entity); assert it.exercise() == expectedResult; assert it.currentState == expectedState; 实现。马丁称之为:Test Isolation