I am using a combination of Spring and Hibernate in my project and would like to test the DAO methods like Save and Delete methods.
daoFoundation is a wrapper class created over hibernateSession.
#Override
public String createSubject(Subject subject) {
String subjectId = (String) daoFoundation.save(subject);
return subjectId;
}
This is what I wrote in my JUnit Runs with SpringJunit4ClassRunner
I created the subject object in my SetupMethod.
#Test
public void createSubjectTest(){
subjectDao.createSubject(subject);
assertNotNull(hassSubjectSelection.getId());
}
Is this sufficient or do I need to write anything additional in my test class?
The easiest way is to import your Spring application context, autowire in the DAO's you want to test and then mark either your test methods or the entire class as #Transactional. This will create a Hibernate session, run your test and then automatically roll back the transaction so you don't effect your database state with your tests.
Have a look at how to run unit tests with Spring here. You can get Spring to create your entire application context using the #ContextConfiguration annotation. So if you create your database using an XML file called database-servlet.xml then you would annotate
#ContextConfiguration(locations={"classpath:/database-servlet.xml"})
public class Test()
You can use the annotation #RunWith(SpringJUnit4ClassRunner.class) to use functionality of the Spring TestContext Framework with your unit tests. This allows you to do things like declare expected exceptions that should be thrown, run timed tests, repeat test runs X times and a bunch of other cool stuff.
Basically to get this working, your test class should look similar to the following:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={YOUR APP CONTEXT FILES HERE AS A COMMA SEPARATED LIST})
public class Test(){
#Autowired
private YourDAO yourDAO;
#Test
#Transactional
public void testSave(){
//Test save method here. Any database changes made here will be
//automatically rolled back when the test finishes.
}
Let me know if that works or not.
The best way to test your dao layer is to use the spring jdbctemplate to write data to your database the test your get and delete methods. Then in the #after delete the records you wrote. Then use hibernate to write to your database and use jdbctemplate to read them back. Then delete your test rows. Anything less and all you are really doing is testing hibernate's caching.
Related
I'm implementing a project using SpringBoot, JPA and Hibernate.
I implemented the DB entities layer with JPA repository.
I'm interested to understand the best practice to write unit-tests for this layer.
Point number one: for this layer, from your point of view, it's necessary to use an integrated DB or it' necessary to mock using, for example, Mockito?
My idea, for this layer, it's, for example, to test the entity structure: check fields validation for example, insert and retrieve some data. In this way, I think I could cover the tests for this entire data-layer.
I'm trying to understand these best practices and, in the mean time, I tried to write a first example of the test:
#ActiveProfiles("test")
#DisplayName("Test Item JPA Entity")
#DataJpaTest
#AutoConfigureTestDatabase( replace = AutoConfigureTestDatabase.Replace.NONE )
public class ItemEntityTest {
#Autowired
MyEntityRepository repo;
#Test
#Transactional
public void testEntityCreation() {
Entity e = new Entity();
e.setMyField1("A");
e.setMyField1("A");
//e.setMandatoryField("C")
repo.save(e);
}
}
Unfortunately, In this case, I notiest that the fields validation is not applied (#NotNull or #NotEmpty, or #Column(nullable=false), etc ... If I try to save the entity into my application the validation works fine... the exceptions are raised). Why?
Also some "automatic fields" (for example creation time and last modification time) are not filled.
Is this the correct path? Ho to test my entities definition?
As mentioned in that answer, the problem is that with #DataJpaTest spring will use TestEntityManager and the transactional annotation will override the default auto commit and rollback behaviour by spring boot.
So your test method will pass and assuming the hibernate is the ORM being used here, when the flushing will take place finally, hibernate's pre-insert event will fire and validations will be applied, but your test case will pass till then so it will produce false positive (as the terms used in spring docs)
Solution: You would need to inject entity manager in your test and flush manually it so that hibernate pre-insert event triggers before your test completes.
#ActiveProfiles("test")
#DisplayName("Test Item JPA Entity")
#DataJpaTest
#AutoConfigureTestDatabase( replace = AutoConfigureTestDatabase.Replace.NONE )
public class ItemEntityTest {
#Autowired
MyEntityRepository repo;
#Autowired
private TestEntityManager em;
#Test
#Transactional
public void testEntityCreation() {
Entity e = new Entity();
e.setMyField1("A");
e.setMyField1("A");
//e.setMandatoryField("C")
repo.save(e);
em.flush();
}
}
This must trigger your validations on the entity applied, this is documented in spring framework docs.
Please notice that the documentation is about spring framework and uses session factory, but the concept is same
You may check the spring boot docs as well, which points to the spring framework docs for this behaviour.
I'm writing a #Repository/#Service integration test that leverages an embedded database. In my test class, I would like to preload my database with some data.
I'm currently using #BeforeEach to load in my sample data, however, this code is run upon each test in my class.
Is there any way that I can load in my test data after Spring application context has loaded, but before any test has been run?
My current approach:
#BeforeEach
public void before() {
repository.save(...); // -> prepopulates repository with sample data
}
#Test
public void testService() {
service.get(...); // -> gathers existing record
}
#Test
public void deleteById() {
service.delete(...); // -> deletes existing record
}
However... with this, I am required to flush out the records after every test. Otherwise any unique constraints can easily be violated.
Rather than using #BeforeEach which is required to run before every test... is it possible to load this in in a #BeforeAll kind of fashion that happens after the spring application context has been loaded?
Is there any way that I can load in my test data after Spring application context has loaded
Basically yes, I think you can do that:
The idea is to load the SQL data when the application context is started or in the process of being started.
For example, spring boot integration with Flyway works this way (the bean of Flyway is created and loaded). So, in theory, you could merely use Flyway with test migrations that will contain all the relevant SQL scripts of test data generation.
How can you do this technically?
Here is one way:
Create a special bean (just like the way it works with Flyway) that would depend on your repository and in post construct save the data:
#Component
public class SqlGenerationBean {
#Autowired
private MyRepository repo;
#PostConstruct
public void init() {
repo.save();
}
}
Another way of doing is to create a listener that will be called upon the application context started and again will call the same repo.save().
In both cases the bean/listener code should not be accessible from production (it's only for tests): so put it somewhere under src/test/java for example
Now once the application context is started you can use a neat trick:
Mark your tests with #Transactional annotation. Spring will wrap the code in an artificial transaction that will be rolled back automatically (even if the test succeeds) so that all the data that you'll modify during the test will be rolled back and basically before each test, you'll have the same state (that is identical to the state of the database when/after the application context starts). Of course, if you use DDL in the test, some databases can't make it a part of transaction but it depends on the database really.
Another interesting point here is that the application context can be cached even between the test cases (created only once), so keep this in mind.
In this case I would just create a constructor for the test class. It will be triggered before everything.
#BeforeEach runs before each tests but after all initialisations .
you can also just use Mockito and mock the result without need to clean and overcomplicate
Just add following snippet to your code. This is just like you can do to detect that Spring application is really started.
#Configuration
public class AppConfig implements ApplicationListener<ApplicationReadyEvent> {
/**
* This is to indicate in the logs when the application has actually started and everything is loaded.
*/
#Override
public void onApplicationEvent(ApplicationReadyEvent event) {
ApplicationContext context = event.getApplicationContext();
Environment env = context.getEnvironment();
// do what you want on application start
}
}
P.S. For database manipulation in test #Sql is the best candidate as was mentioned in comment.
I am trying to unit test my Service classes that looks similiar to this:
#Service
public class SomeQueryService {
private final SomeRepository repository;
public SomeQueryService(SomeRepository repository) {
this.repository = repository;
}
public void doSomething() {
// code doing some magic
}
}
SomeRepository is simple repository interface extending JpaRepository interface.
What I want to do is unit test this service to verify whether it is working properly.
I do not want to use mockito to mock repository behaviour instead, I want to have some in-memory implementation (on list or map) that will imitate database behaviour.
Does Spring provide such fake implementations?
I want to avoid making Stub Implementation of such repository by myself as I will be using such tests in many other places.
RealLifeDeveloper has created an MIT-licensed helper-class to do just what you want: implement the repository-interface with a plain-old-java-object that just wraps a Collection, and called it "InMemoryJpaRepository". You will still have to implement some logic yourself1, though it should be easy if your queries are not too complicated.
An article explaining how to do this with example: https://reallifedeveloper.com/creating-in-memory-versions-of-spring-data-jpa-repositories-for-testing/
The repository (which includes other stuff, too) is on github: https://github.com/reallifedeveloper/rld-build-tool
The specific helper-files for creating the inmemory-db are found at https://github.com/reallifedeveloper/rld-build-tools/tree/master/src/main/java/com/reallifedeveloper/tools/test/database/inmemory if you dont want the whole repo.
1 The rule "Don't put logic in tests" exists for a reason and is clearly violated here. However, the well-known and widely-used alternatives mentioned by the other answers, H2-testing and extensive mocking, have their drawbacks too.
The type of testing you are referring to is called "Integration Testing" or "End to end testing" since it tests the whole application or a big chunk of it compared to unit tests that test only one method.
https://www.guru99.com/unit-test-vs-integration-test.html
You should not unit test your repositories, since they are already well tested by the spring team.
Solution:
You can create a test that starts the whole spring container using Spring Boot:
Just create a class in your test folder and annotate it with:
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyTestClass {
#Test
public void test() {
}
}
You can then configure an embedded database using H2 so that your test does not use the production database, just follow the Spring Boot Database Initialization doc.
https://docs.spring.io/spring-boot/docs/current/reference/html/howto-database-initialization.html
PS. You can also create a specific profile using the following annotation on your test class:
#ActiveProfiles("test")
No Spring Data does not provide a fake implementation of that interface.
You'll have to create it on your own or use a mocking framework like Mockito.
I have created small web application in spring boot.I am new for TestNG. I am trying to test my services with testng which calls dao for database operations. I am trying to do it using inmemory database HSQL.
Following is my UserService
#Service
class UserServiceImpl implements UserService
{
public void save(User user)
{
userDao.save(user);
}
public User update(user)
{
userDao.update(user);
}
}
Following is my UserTest class
#Test
class UserTest
{
?
}
What is good way to use HSQL to test save and update methods in UserService using TestNG with DataProvider? Please let me know if need some more information regarding question ;)
Your response will be greatly appreciated!!
If you just want to test if the dao is called correctly, mock it with a mocking framework (i'd choose Mockito) and just verify that the correct methods were called by your Service. This is more "unit" like, since you tests reflect the clear separation of dao and service.
If you are interested in real DB communication to create/load instances and so on, you can use an in memory database like h2 and let your dao run against that database. This becomes more of an integration test, but can be useful none the less.
Either way, you would set up a test application context that cares about datasources and mocks.
Acolyte provides a JDBC driver & tools, designed for such purposes (mock up, testing, ...): https://github.com/cchantep/acolyte
It's already used in several open source projects (Anorm, Youtube Vitess, ...), either in vanilla Java, or using its Scala DSL:
handler = handleStatement.withQueryDetection(...).
withQueryHandler(/* which result for which query */).
withUpdateHandler(/* which result for which update */).
// Register prepared handler with expected ID 'my-unique-id'
acolyte.Driver.register("my-unique-id", handler);
// then ...
Connection con = DriverManager.getConnection(jdbcUrl);
// ... Connection |con| is managed through |handler|
Note: I am the author of Acolyte.
I am trying to use dbunit to test the database of the system. Since several testcases can be tested using same dataset, I want to init the dataset once for all of them. But I also use #AutoWire of spring to init those db connection parameter.
I tried to use #BeforeClass and #AfterClass to setup the db. But turns out the #Autowire happens when class get initiated(not seems not work for auto wire static members).
So wondering is there any other way i can setup/tear down the db dataset before/after all the testcases?
I found one elegant solution:
How to allow instance methods to run as JUnit BeforeClass behavior
This post basically explained a way to change unittest runner to trigger the events.
And for spring, the AbstractTestExecutionListener can be used as well
Just initialize database using the ApplicationListener interface. Please take a look at this question:
How to add a hook to the application context initialization event? It is possible to create all the data in the onApplicationEvent method.
Anyway don't use dbunit, just create all your tests with #Transactional, and #DirtiesContext (DirtiesContext on the class level) using ClassMode=AfterEachTestMethod
So the code would be like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "classpath:testContext.xml" })
#DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
public class YourTestClassTest {
//...
}
I also use DBUnit for my integration tests and i setup/teardown the data within the #Before and #After annotated methods instead of #BeforeClass/#AfterClass. so each test gets its refreshed test-data.
To use different datasets for each tests or different replacements for a single test you also could call a setup(dataSet) or setup(replacementList) method as first row of your #Test annotated method (than you don't us #Before annotation).