I have simple crud repository:
public interface BookRepository extends CrudRepository<Book, Long> {
#Modifying
#Transactional
void deleteById(Long id);
}
How can I write simple JUnit test to check if this method works fine? Of course, in this case, I have entity Book
You should not.
Unit testing Spring Data repositories is a problem of Spring Data developers - as a developer you are using their API and it should working that's all. Care about your logic not the library - focus on integration tests
Anyway if you really want to do this - what I would do would be to mock EntityManager and somehow inject it to the Repository implementation (sounds like horrible task), or I would use DBUnit test, prepare test data and simply would check whether object was deleted (this would be prefered way)
Usually Spring Data repositories do not tests, but If you really want to test it you can use #DataJpaTest and inject TestEntityManager.
Example:
#DataJpaTest
class BookRepositoryTest {
#Autowired
private BookRepository repository;
#Autowired
private TestEntityManager em;
#Test
void deleteById() {
Book book = ...;
final Long id = em.persistAndGetId(book, Long.class);
repository.deleteById(id);
em.flush();
Book after = em.find(Book.class, id);
assertThat(after).isNull();
}
}
Idea is that you use TestEntityManager for all operation exclude your. In the example you are persist and find entity via TestEntityManager but delete via your custom repository.
Related
I have a Spring Boot project and I want to test some queries. I want to insert a predefined set of data and execute the Repository query to check the result is the desired one.
To do this I'm using in-memory H2 DB and the problem (I think) is not there, everything related with DB is ok. The main problem is I can't mock properly the EntityManager field in repository and the query is always null.
My repository is like this:
#Repository
public class MyRepositoryImpl implements MyRepository {
#PersistenceContext
private EntityManager entityManager;
#Override
public Result runQuery() {
TypedQuery<Result> query = entityManager.createQuery(
"SELECT ...", Result.class);
return query.setParameter("...", "...") // here 'query' is always null
.setMaxResults(1)
.getResultStream()
.findFirst()
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Entity not found"));
}
}
It works nice when is executed out of tests, but trying to run this test file it throw an error:
#RunWith(SpringRunner.class)
public class MyRepositoryTest {
#Mock
EntityManager entityManager;
#InjectMocks
MyRepositoryImpl repository;
#Test
public void it_should_works() {
Result r = repository.runQuery();
assertNull(r);
}
}
The repository is mocked and is not null, I can call the method. But inside the repository, as query field is null, it throw a NullPointerException when try to execute.
I've searched over the internet and I've found many ways to test the JPARepository and #Query inside the interface, but not an EntityManager query.
Also I've found a few ways to mock the result for the query, something like when(runQuery()).thenReturn(result) but I don't want that, I've the data in the memory DB so I want to execute the query and get the result.
So, now, the main problem I think is how to mock the EntityManager object properly inside repository class.
Thanks in advance.
Edit:
I've follow this link and is like another SO questions: It's only to mock the JpaRepository.
I've used this code:
#Test
public void it_should_works() {
Result r = repository.findAll();
assertNotNull(r);
}
And works perfectly, but using my own query fails with error:
org.springframework.orm.jpa.JpaSystemException: could not advance using next(); nested exception is org.hibernate.exception.GenericJDBCException: could not advance using next()
...
Caused by: org.h2.jdbc.JdbcSQLNonTransientException: El objeto ya está cerrado
The object is already closed [90007-200]
So the question is: It is related to my DB? Why using JpaRepository method it works but my own query don't?
Edit:
Solved adding #Transactional into repository.
Since you're using an h2 in-memory database to run tests, and you want to actually use that database in your tests, you shouldn't really be mocking anything.
Your mocking doesn't work because the MyRepositoryImpl is typically initialized by Spring, and that process is much more complicated than plugging in an EntityManager.
I think what you want to do is more like what's described here https://www.baeldung.com/spring-testing-separate-data-source
So you would have a src/test/resources/application.properties file that overrides the datasource properties. Then you just #Autowired your repository into your test class like normal.
I want to write the junit for EntityManager but unable to mock the EntityManager. My service class method listed below:
#PersistenceContext
private EntityManager em;
public List<Items> getItems() {
Order order = Order.desc("ASC");
Criteria crit = em.unwrap(Session.class).createCriteria(Items.class);
Criteria critRowCount = em.unwrap(Session.class).createCriteria(Items.class);
crit.add(Restrictions.eq("createdBy", "John"));
critRowCount.add(Restrictions.eq("createdBy", "John"));
crit.setFirstResult((pageNo - 1) * pageSize);
crit.setMaxResults(pageSize);
crit.addOrder(order);
List<Items> items = crit.list();
critRowCount.add(Restrictions.sqlRestriction("Query"));
critRowCount.setProjection(Projections.rowCount()).uniqueResult();
return items;
}
I used in my tast case:
#MockBean
private EntityManager em;
and when I called below method in service
List<Items> items = crit.list();
This will hit DB. Please halp me to resolve the problem.
Thanks in Advance!
I have no idea why the mocking does not work (from what you present here, I'd say it should work). But I would like to suggest another approach. I suppose the getItems() method resides in some kind of Repository or DAO or whatever you call this kind of thing. For unit-tests I would usually mock them. If I would want to test getItems(), I would actually try to test it through the database (e.g. via testcontainers) in an integration test, because this rather close to the database anyway.
In your concrete case I would probably not even try to test getItems(), since it only seems to contain some plumbing code.
As a rule of thumb: It is usually not a good idea to mock what you do not own.
I'm learning tests and I consider situation when i have class like that:
#Service
#RequiredArgsConstructor
#Transactional(readOnly = true)
public class SearchCustomerService {
private final CustomerRepository CustomerRepository;
public Page<CustomerDTO> search(CustomerCriteria criteria, Pageable pageable) {
CustomerSpecification specification = new CustomerSpecification(criteria);
return customerRepository.findCustomers(specification, pageable);
}
}
How tests should looks like? I think unit tests are not that really good because what am I actually testing? I'm just getting some data from database and if I mock repository.. then I test Mockito, right?
You can create integration test that includes persistence layer (eg inmemory H2), service and meaningfull dependencies, where your input would be a proper set of Customer entities in the repository, and assert outcome of invoking search based on some criterias.
To make it a unit test, I can you could only assert the shape of CustomerSpecification but that will not guarantee that specs itself are valid for given case - thus makes small sense.
This case is similar to testing native queries execution. You don't assert SQL string, but assert result having test data set.
I am migrating from spring-boot 1.5.x to 2.0.4 and noticed an interesting case/behaviour with #DataJpaTest
I have a test
#ExtendWith(SpringExtension.class)
#DataJpaTest
public class SomeTest {
#Autowired
private SomeRepository repository;
#Autowired
private JdbcTemplate template;
#Test
public void save() {
String number = "123";
SomeEntity entity = SomeEntity.builder()
.number(number)
//some attributes here
.build();
repository.save(entity);
//that line fails because SELECT returns nothing back
Map<String, Object> result = template.queryForMap("select id, version from some_entity where number=?", number);
}
This test above fails because nothing is returned from the template. I can't see even INSERT statement fired in the logs.
But with old version of spring-boot of 1.5.x I can see the INSERT been fired and test passes.
But the interesting thing with upgraded version is if I add a line repository.findAll (not repository.findById(id) -> that one won't help) before repository.save(entity) it all works good and in the logs, I can see and INSERT statement fired.
Can someone please help me to understand what's going on and how it works under the hood.
Why the entity has not been persisted? and why repository.findAll is so special that makes it to persist the data.
Thanks.
It happens because findAll method triggers flush of all changes from memory(persistence context) to database.
You can see in my screen the place where it all happens:
There is also a corresponding method in JpaRepository:
https://docs.spring.io/spring-data/jpa/docs/current/api/org/springframework/data/jpa/repository/JpaRepository.html#flush--
It may be used to flush changes immediately.
Additionally, you may use saveAndFlush method:
https://docs.spring.io/spring-data/jpa/docs/current/api/org/springframework/data/jpa/repository/JpaRepository.html#saveAndFlush-S-
I am using spring data jpa for my project and i have following pieces of code:
My Repository:
#Repository
#Transactional
public interface StudentRepository extends PagingAndSortingRepository<Student, Long> {
List<Student> findByIdIn(List<Long> ids);
}
and my entity is :
#Entity
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
// other fields
// getters/setters
}
Somewhere in my service class, i have
#Autowired
StudentRepository studentRepository;
and then i call findByIdIn from my service layer like :
studentRepository.findByIdIn(listOfIds);
findByIdIn(listOfIds) method works perfectly fine and everything is working as expected.
I know that the implementation of findByIdIn() method is provided by spring data jpa.
But i am not able to figure where is its implementation present?
What exactly is its implementation?
Are such methods generated at run time depending upon the method-name? If yes how are they generated and executed dynamically?
Thanks!
You can dig a little bit in the core of the Spring code to see how it works (https://github.com/spring-projects/spring-data-commons/tree/master/src/main/java/org/springframework/data/repository), but basically, it's parsing the interface methods into HQL at the startup time.
You can test just be editing a method name to a field which doesn't exist, and you'll get an error at startup time saying that there is no field as this.