简体   繁体   中英

Testing DAO and Service layer with in-memory db and mocking

During my learning of testing in Java, I faced a thing which I didn't really get. So, basically, I have unit tests for DAO where I test CRUD operations with the help of in-memory database HSQL.
Well, the next step is to test the Service layer. On the web, they say to use mocks to mock DAO and then test Service.

I see only one way to use mocks: if there are some methods in Service and those methods don't exist in DAO. Then, we don't need to test CRUD operation(DAO itself), therefore we mock DAO and test only Service.

  1. But what If I have the same methods in Service as in DAO?
  2. And what If I just use an in-memory database to test Service as I did with DAO?

I'd like to know what are the benefits to use mocks instead of an in-memory database . Moreover, I guess, it is less readable when use Mocks.

If it makes any sense - I use Spring.

This is a broad and potentially contentious topic…so be prepared for a wide variety of answers and divergent view points…

The advantage of in-memory databases:

  • Might reduce one set of integration tests that are designed to ensure that you're avoiding lazy/explicit loading cases and that you're getting properly formatted entities back.
  • In some cases it might reduce the setup code required in each of your test methods. eg if you have to set up a dozen mock calls on a few different mocks.
  • Can sometimes be easier to reason about the test when understanding what is being tested

Disadvantages of in memory databases:

  • Lacks full isolation for your unit tests
  • Requires your DI container to be initialized for unit tests.
  • May provide false sense of security depending on how different the in-memory database provider is from your production database,

The advantage of mocks:

  • Fully isolate code under test
  • Can set up just what is needed to support your tests. eg don't need full and complete entities configured.
  • Tests often run faster and you can completely ignore your DI framework for the unit testing (part of isolation but worth mentioning).

Disadvantages of mocks:

  • Test code may become complex and brittle depending on how cohesive the service methods are, and how they are coupled to the DAO layer. ie if you need to set up 20 mock method invocation and three mock objects, that quickly becomes tedious (however, that should be a sign that your service needs to be refactored).

To answer the first question you asked about what happens when you have the same method in both -- easy answer. They aren't the same method. They're doing two different things. One's communicating with the database, the other is applying business logic. If you do have a pass through, you should still probably throw a test on it in the service layer too, to make sure it's calling the right thing on your DAO.

No matter which way you go, don't forget to also integration test your code!

Speaking from experience (of having written thousands of automated tests):

  1. Avoid mocking as much as possible, as it's not conducive to good, proper tests. Usually, tests that use mocking are written from the wrong point of view, where the developer thinks only of executing the implementation code, rather than checking meaningful business-oriented behavior. Good tests are written from requirements , not merely to exercise code.

  2. Integration tests can be fast enough (what really matters is that developers don't get discouraged from running them often), and they can be made to run in rollback-only DB transactions, therefore ensuring test isolation.

  3. Use your development database (usually a shared remote db using the same db engine as in production, where the application can be manually tested as well) rather than an in-memory db. This way, you avoid the expensive in-memory db schema creation on every test run (assuming the dev db is always up and running, with all tables, which is normally the case), and any potential issues caused by using two different db technologies.

The above describes the approach I use in Java web applications (using Java EE or Spring, doesn't matter) having hundreds of tables in a relational db (accessed with JPA/Hibernate). There is only one test suite of out-of-container integration tests, where each test runs in a single db transaction which is always rolled back. Some tests do a bit of mocking, in particular for calls to remote web services, but that's it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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