Join Shonna Smith for an in-depth discussion in this video Write integration tests for @Repository, part of Spring: Test-Driven Development with JUnit.
- [Instructor] Now we're ready to write some integration tests for our Spring repository components. Since this is an integration test, we want to see how well our contact management data access component puts data into the database or retrieves it. As usual, there's a bit of setup to do. We are only focusing on testing our repository components, so we want to tell JUnit not to load any controllers or service components, and for this type of testing, a new setup need is we have to load JPA testing configurations.
So let's started, and you'll see what that means. So here in our exercise project, I'm going to drill down into source test Java tree, and down into the data.repos package, and we want to open up this CustomerContactRepositoryIntegrationTest. I'm going to double-click on this tab so we can maximize this window. So in terms of setup needs, you'll see the very familiar RunWith annotation.
What's new for us now is the annotation for DataJpaTest. This is a shortcut annotation that gives us a lot of the things we need for testing JPA code. There's also a new annotation here, AutoConfigureTestDatabase. This is letting us set up which type of database we want to use for our testing. We have the option of using an embedded database. We also have the option of just using the data source we already have configured here in our local environment, and for the sake of simplicity, that's the one I'm choosing.
Something I also want to call your attention to. We're injecting our TestEntityManager, and you'll see why that's useful in a second, and we're also injecting or auto-wiring the CustomerContactRepository component because that's the component we want to test. The test case we're working on now is testFindByEmail, and what I want to do is set up a scenario where we're creating a contact that has an email address, and I already have someone in mind.
I'm just going to make up an email address here, firstname.lastname@example.org, and that's all I really need for that contact so far. So I'm going to copy that email address, and I already have the line of code where I want to test my findByEmail or my customerContactRepository. I'm just going to paste in that email. So that's what I want to find, but let me jump back.
I have to make sure it's there in the database first, and the way I'm going to do that for this test is I'm going to take advantage of the fact that I have access to our entity manager, which allows me to directly persist or save this new contact. So now this new contact with this email address that I just made up, it's going to be saved directly by the entity manager, and I have my line of code set up, where I'm calling the FindByEmail on my repository, and then I have an assert here already set up too.
Now I'm going to copy and paste in this email just to avoid any mistypes. Copy, paste. So, now I'm going to run this test, and what I expect to see is of course JUnit green bar. Let me go over here in my package explorer. I'm going to expand my test class, and go right-click on the method that I want to run as a JUnit test.
I run it, I wait a bit, and I see the green bar, and that's exactly what I wanted to see. So I saved a new contact, I was able to find it, I was able to assert that it was there, and I got what I needed. So let's do one last thing. We want to prove that our database was left in a good state. So I already have a query ready to go here. Well, I'm going to select the contact ID and the contact first name from the customer contact table.
Let's run this query, and we get back nothing, an empty result set. That's exactly what I expected. This lets us know that we were able to run a test that altered the database and by the end of that test, the database was rolled back to its original clean state. Let's review what we just accomplished here. Yes, we created a test class for our data repository. It was an integration test, but we also configured a test database that had transactional behavior, and we'll see the proof of that in a second.
We asserted our test database record, where we were able to access the test entity manager to do so. What this allowed us to do is not only test that our findBy method was working but it is a form of entity testing. Remember, the CustomerContact object is an entity. What we're doing here is making sure that all of the mapping is correct between an entity and the table that it represents, but a whole lot more happened that you may not have seen so we're going to review a little deeper.
That DataJpaTest annotation, it packed a whole lot of magic in one single annotation. It's what I like to call one of those combo Spring annotations, which is really a shorthand way of adding a whole bundle of annotations that tend to go well together. I've listed a few here, but actually the DataJpaTest annotation is a shorthand for several more. Now I'm going to go over the ones that we got the most benefit from.
First there's the AutoConfigureDataJpa annotation. This is a pretty basic need here. You're letting your test environment know that, hey, I'm about to start doing a lot of Jpa testing. So I need to know all of the components that make sense for this type of test objective. Next, the AutoConfigureTestDatabase. That annotation allowed us to specify what type of test database we want to use. The options were an embedded database, or porting to an external database, or simply accepting the default database that's already configured and found on our class path during execution of the test code.
There's also configuring the TestEntityManager. As you saw in that test example, we had access to an entity manager that allowed us to pretty quickly create an entity and have it persisted in the database. Lastly, we got a transactional annotation as a result of using that one shorthand DataJpaTest annotation. That meant we got rollback behavior. So we were able to expect the database to be in a certain state when the test began, and also know exactly what that state was when a test ended.
This means our tests are repeatable. Now when testing out the data access layer, repeatability can definitely be tricky. We want to make sure we begin and end a test with the database in a known state, and with the help of a little Spring and JUnit combination magic, we've done just that.
- Why test-driven development matters
- Test planning for @Service, @Controller, and @Repository components
- Writing integration tests for @Service, @Controller, and @Repository components
- Writing unit tests for @Service, @Controller, and @Repository components
- Creating integration test datasets
- Making a feature test suite
- Making a continuous integration test suite