简体   繁体   English

单元/集成测试nHibenrate查询

[英]Unit/integration testing nHibenrate query

Scenario: I need to write a complex nHibernate query, that would return projected DTO, but I want to use TDD approach. 场景:我需要编写一个复杂的nHibernate查询,它将返回投影的DTO,但我想使用TDD方法。 The method would look like this: 该方法如下所示:

public PrintDTO GetUsersForPrinting(int userId)
{
     Session.QueryOver<User>().//some joins, conditions etc.
     //returns projected dto
}

Questions: 问题:

  1. Since the most common approach is to use in memory database for this kind of operations. 由于最常见的方法是在内存数据库中使用这种操作。 Should I write integration test? 我应该编写集成测试吗?
  2. If I am using in memory db can I write Unit tests? 如果我在内存中使用db,我可以编写单元测试吗?
  3. Is one test is enough? 一次测试就足够了吗?
  4. Since my integration test probably will check projection, how should I name it? 由于我的集成测试可能会检查投影,我该如何命名呢? "GetUserForPrinting_return_correct_DTO" seems too abstract and silly. “GetUserForPrinting_return_correct_DTO”似乎过于抽象和愚蠢。

I ask because: 我问因为:

  • There is lots of abstract information about TDD and integration testing, but when it comes to concrete implementation it is very difficult to apply that information. 有很多关于TDD和集成测试的抽象信息,但是当涉及具体实现时,很难应用这些信息。
  • TDD suggests that integration test should be made of unit tests: TDD建议应对单元测试进行集成测试: 集成/单元测试

This is not really a very good problem to learn TDD with. 学习TDD并不是一个非常好的问题。 I assume you don't already know what the complex query looks like, and you want to use test-driven techniques to drive it out. 我假设您还不知道复杂查询的外观,并且您希望使用测试驱动技术将其驱逐出去。 Awesome :) 太棒了:)

But let's see if I can answer your questions. 但是,让我们看看我是否可以回答你的问题。

  1. Yes

  2. any test that includes a real db, whether it is in-memory or on-disk, is not a unit test. 任何包含真实数据库的测试,无论是内存还是磁盘,都不是单元测试。 A unit test would use a mock db. 单元测试将使用模拟数据库。

  3. Maybe - if you query is complex enough, then no. 也许 - 如果你的查询足够复杂,那么没有。

  4. testGetUsersForPrinting or getUsersForPrintingTest or similar testGetUsersForPrinting或getUsersForPrintingTest或类似的

Most probably I would drive out the query in a SQL interpreter, not in code. 最有可能的是,我会在SQL解释器中驱逐查询,而不是代码。 The aim would be to produce a series of integration tests against an in-memory db based on what I learn during this process. 目标是根据我在此过程中学到的内容,针对内存数据库生成一系列集成测试。 Start from the minimum possible DTO you can think of, and build up from there. 从你能想到的最小可能的DTO开始,然后从那里开始构建。

Finally convert the query into nhibernate calls, then make the integration tests pass. 最后将查询转换为nhibernate调用,然后使集成测试通过。

Test-driven, but not really unit-test-driven. 测试驱动,但不是真正的单元测试驱动。

If you are willing to accept maximum TDD discipline and deal with working slower and being more annoyed than usual, you can automate each integration test as you develop it and write code to make it pass. 如果您愿意接受最大的TDD规则并处理工作速度较慢并且比平时更烦恼,您可以在开发时自动执行每个集成测试并编写代码以使其通过。 This will mean you are switching frequently among 3 levels of abstraction / editors / environments (direct SQL queries, integration tests, c# code) - I deal with this by setting up techniques to force myself to follow the right steps each time. 这意味着您经常在3个抽象/编辑/环境级别(直接SQL查询,集成测试,c#代码)之间切换 - 我通过设置技术来强制自己每次都遵循正确的步骤来解决这个问题。

This last bit is why this is not a good problem to learn TDD with. 最后一点是为什么学习TDD不是一个好问题。 You will need a lot of discipline you probably haven't forced yourself to acquire yet! 你需要很多纪律,你可能还没有强迫自己获得!

Good luck. 祝好运。


ok some concrete examples. 好的一些具体例子。 I would modify your code sample to look like this 我会修改你的代码示例,看起来像这样

public PrintDTO GetUsersForPrinting(int userId, ISession session)
{
     var data = session.QueryOver<User>().//some joins, conditions etc.

     return data; // or whatever
}

In your unit test you would write 在你的单元测试中,你会写

public testDTO()
{
    //Arrange
    StubSession session = .... setup a stub session, which returns hardcoded values

    // Act
    PrintDTO users = GetUsersForPrinting(111, session);

    // Assert
    Assert.That(users.size(), Is.EqualTo(1));    
    Assert.That(users.get(0).userId, Is.EqualTo(111));

}

In your integration test, you would use a real db, and your session object would actually connect to it, and the queries would be resolved against that db 在集成测试中,您将使用真正的数据库,并且您的会话对象实际上将连接到它,并且将针对该数据库解析查询

Arrange-Act-Assert is a standard method for organizing unit tests. Arrange-Act-Assert是组织单元测试的标准方法。 Generally you want as few Asserts as possible in a unit test. 通常,您希望在单元测试中尽可能少的Assert。 And you will have multiple unit tests. 你将有多个单元测试。 When you are writing a unit test, start by writing the Assert, then fill in the rest to make it compile/get the result you want. 在编写单元测试时,首先编写Assert,然后填写其余内容以使其编译/获得所需的结果。 Make the test fail first, because then you know you have really delivered something when it passes. 首先让测试失败,因为那时你知道你确实在它通过时已经交付了一些东西。

In this example to implement a stub ISession you would derive a local StubSession class (only visible to the test suite) from ISession and just fill in the absolute minimum to get it to compile, and return the minimum data to get the test to pass. 在这个实现存根ISession的例子中,您将从ISession派生一个本地StubSession类(仅对测试套件可见),并填写绝对最小值以使其编译,并返回最小数据以使测试通过。


To build up to your whole DTO - assuming you know what you want in your DTO - proceed, as you say in the comments, incrementally. 为了建立你的整个DTO - 假设你知道你在DTO中想要什么 - 按照你在评论中的说法逐步进行。 Build up each part of your DTO a piece at a time, add a unit test for each piece. 一次构建DTO的每个部分,为每个部分添加单元测试。

Keeping track of this is another piece of TDD discipline. 跟踪这是另一项TDD纪律。

Set yourself up with a TODO list - just a simple text file, or possibly a lengthy comments at the start of your test suite. 使用TODO列表设置自己 - 只需一个简单的文本文件,或者在测试套件的开头可能有冗长的注释。 List all the things you want to test eg zero results, one result, two results, 20 results. 列出您要测试的所有内容,例如零结果,一个结果,两个结果,20个结果。 User id, whatever other pieces of information you need to have. 用户ID,无论您需要具备哪些其他信息。 If you are doing a complex query across tables or whatever add an todo item for each join, each part of the where clause, etc. Add items for ordering and paging etc if you are using those. 如果您正在跨表执行复杂查询或为每个连接添加todo项,则为where子句的每个部分等。如果您使用这些项,则添加用于排序和分页的项目。

Pick the simplest things first. 先选择最简单的东西。 Only do one small thing (in a single red-green-refactor cycle) at a time. 一次只做一件小事(在一个红绿重构循环中)。 As you work through your list, you might want to break items up into smaller pieces, or you might think of additional things you need to do. 在处理列表时,您可能希望将项目拆分为较小的部分,或者您可能会想到需要执行的其他操作。 Add them to the TODO list rather than working directly on them. 将它们添加到TODO列表中,而不是直接在它们上面工作。

In this particular case I would swap - after each red-green-refactor cycle - into the SQL environment and/or the sqlite integration test to work out how to make the next piece work. 在这种特殊情况下,我会在每个红绿色重构周期之后交换到SQL环境和/或sqlite集成测试中,以确定如何使下一个工作。 I guess this is a sort of step between red and green - choose what you will test next, write the test (which fails obviously), fiddle around in SQL until you know how to make it pass, write the nHibernate calls to make your test green, then refactor. 我想这是红色和绿色之间的一种步骤 - 选择接下来要测试的内容,编写测试(明显失败),在SQL中解决问题,直到你知道如何使它通过,编写nHibernate调用来进行测试绿色,然后重构。

Be aware some of the things you list might run out not to be necessary, or take too long, etc. It's good to write them down still, so you know what are not doing as well as what you are doing. 请注意,您列出的某些内容可能会耗尽,或者需要花费太长时间等等。将它们写下来仍然很好,所以您知道哪些内容与您正在做的不一样。 Keep focused on your goal. 专注于你的目标。

I tend to also develop a list of "smells" and/or refactorings that I can see I will want to do but am not quite ready for this cycle. 我倾向于开发一个“气味”和/或重构的列表,我可以看到我想要做但但还没有为这个循环做好准备。 Remember to minimise duplication/refactor your tests as well as your SUT (System Under Test). 请记住尽量减少重复/重构您的测试以及您的SUT(被测系统)。

It's a doing rather then seeing thing. 这是一个做而不是看到的事情。 The list of what unit tests you end up with, and the code they exercise, is not a very good description of the journey. 你最终得到的单元测试列表,以及他们运用的代码,并不是对旅程的非常好的描述。 Kent Beck's original TDD book is slim and will give you some good overall pointers, but not really about constructing queries. 肯特贝克的原始TDD书很渺茫,会给你一些很好的整体指针,但不是真正的构建查询。

Does any of that help? 这有什么帮助吗?

Since the most common approach is to use in memory database for this kind of operations. 由于最常见的方法是在内存数据库中使用这种操作。 Should I write integration test? 我应该编写集成测试吗?

Using in memory database still is an integration test (because it actually tests if your query generates correct SQL and execute it against a database, see ). 在内存中使用数据库仍然是一个集成测试(因为它实际测试您的查询是否生成正确的SQL并对数据库执行它, 请参阅 )。

If I am using in memory db can I write Unit tests? 如果我在内存中使用db,我可以编写单元测试吗?

No, it would be an integration test 不,这将是一次集成测试

Is one test is enough? 一次测试就足够了吗?

Probably not, you should check each condition of your query, for example one test per one where clause, one for paging and one for sorting if applicable. 可能不是,您应该检查查询的每个条件,例如每个where子句一个测试,一个用于分页,一个用于排序(如果适用)。

Since my integration test probably will check projection, how should I name it? 由于我的集成测试可能会检查投影,我该如何命名呢? "GetUserForPrinting_return_correct_DTO" seems too abstract and silly. “GetUserForPrinting_return_correct_DTO”似乎过于抽象和愚蠢。

GivenUserForPrinting_WhenGetUserForPrinting_ThenMapToDTO would be a better naming GivenUserForPrinting_WhenGetUserForPrinting_ThenMapToDTO将是一个更好的命名

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM