I am writing unit and integration tests for my app and I have a problem with testing repositories.
This is very simple repo for handling authorities:
public interface AuthorityRepository {
Authority saveAuthority (Authority authority);
}
#Repository
public class AuthorityRepositoryImpl implements AuthorityRepository {
#PersistenceContext
EntityManager entityManager;
#Autowired
public AuthorityRepositoryImpl(EntityManager entityManager) {
this.entityManager = entityManager;
}
#Override
#Transactional
public Authority saveAuthority(Authority authority) {
entityManager.persist(authority);
return authority;
}
}
I have two questions regarding this code:
Is it wrong approach to not extend any Spring Data Repository interface? Besides not having to write all methods to communicate with database what are other advantages of using it?
How can I test this repository using minimum resources? #DataJpaTest does not work, because (from my understanding) it picks up repos that extend any Spring Data Repository. This works:
#RunWith(SpringRunner.class)
#SpringBootTest
public class AuthorityRepositoryTest {
#Autowired
private AuthorityRepository authorityRepository;
#PersistenceContext
private EntityManager entityManager;
#Test
public void test () {
Authority authority = new Authority();
authority.setName("name");
authority.setUsername("username");
authorityRepository.saveAuthority(authority);
assertNotNull(entityManager.find(Authority.class, 1));
}
But #SpringBootTest is pretty slow as it creates whole application context and I would like to make these tests faster.
I am using H2 database both in main and test, which is declared in application.properties file.
#DataJpaTest works when coupled with #Import(repo.class). It looks like this:
#RunWith(SpringRunner.class)
#DataJpaTest ()
#Import(AuthorityRepositoryImpl.class)
public class AuthorityRepositoryTest2 {
#Autowired
AuthorityRepository authorityRepository;
#Autowired
TestEntityManager testEntityManager;
#Test
public void test() {
Authority authority = new Authority();
authority.setUsername("name");
authority.setUsername("username");
authorityRepository.saveAuthority(authority);
assertNotNull(testEntityManager.find(Authority.class, 1));
}
I learned though, that if I do integration test I could aswell create whole application context and test all things I want reusing this one context, so later when I write more integration tests for other parts of my app I am going to end up using #SpringBootTest for all of these tests anyway.
Thanks for comments, helped me to understand integration tests better.
Related
I can't understand why #Autowiring my #RestController class is returning null.
I want to do a basic unit test before doing an integrated test but its failing.
In fact anything that is being #Autowired is showing null in the test package.
I have a very simple test, I just want to see the basic works:
A very simple example:
#Component
public class SimpleComponent {
public int add(int a, int b){
return a + b;
}
}
And the test:
class SimpleComponentTest {
#Autowired
private SimpleComponent component;
#Test
public void givenSimpleComponentShouldNotBeNull(){
assertNotNull(component);//How on earth does this fail?
}
}
Here is the code from the Controller class:
#RestController
public class EmployeeAccountApi {
#PostMapping("/register")
public String register(){
return "Registering!";
}
}
public class EmployeeAccountApiUnitTest {
#Autowired
private EmployeeAccountApi employeeAccountApi;
#Test
public void testAutowiring(){
assertNotNull(employeeAccountApi);//Fails, Can't understand why
}
}
This works with the #Mock annotation:
But I dont want to mock it as that is the class I am unit testing.
I want to test that the method returns a basic string.
Question is why isn't it working?
#ExtendWith(MockitoExtension.class)
public class EmployeeAccountApiUnitTest {
#Mock
private EmployeeAccountApi employeeAccountApi;
#Test
public void testAutowiring(){
assertNotNull(employeeAccountApi);//This works
}
}
Even if I get the Employee Service that clearly worked and tested in the previous test also is null in this class:
public class EmployeeAccountApiUnitTest {
#Autowired
private EmployeeAccountApi employeeAccountApi;
#Autowired
private EmployeeService service;
#Test
public void testAutowiring(){
//assertNotNull(employeeAccountApi);
assertNotNull(service);
}
}
Why is #Autowired null in the Test?
Mocking is working fine
Plenty of similar questions but they are too specific and not provide any basic information
#Autowired only works if you are spinning up the application context for your application. Annotate the test class with #SpringBootTest, which tells the test to start the entire spring application context, and it should work.
#SpringBootTest has to be used on the test class for Spring to load your ApplicationContext and wire things up. Without out this there's nothing for Spring to inject.
You can use #WebMvcTest on the test class to focus your tests only on the Web Layer.
There's a whole bunch of annotations that allow you to "slice" your ApplicationContext up lots of other ways for testing various other parts of your application.
Spring's documentation and tutorials are really very good. You should check them out.
Testing the Web Layer
I'd like to test my repository method. However when I run my test, it fails because of UnsatisfiedDependencyException. It for some reason tries to create AuthorizationServerConfig (or other bean if I remove #Configuration annotation from this one). It fails cause deeper in dependencies chain it requires RabbitMQ connection pool, that I prefer to not provide in repository test.
The question is why Spring tries to create all those beans not linked to repository logic?
I attempted to exclude all those beans with #DataMongoTest(exludeFilters: ...) and #DataMongoTest(exludeAutoConfiguration: ...) but it had no effect.
The only thing that helped was to add #Profile("!test") to all beans (all! controllers, services, components) in an application, but it smells like a very ugly solution.
Repository class is very simple:
#Repository
public interface ParticipantRepository extends MongoRepository<Participant, String> {
List<Participant> findAllByLoggedInTrueAndAccessTokenExpirationAfter(Date expirationAfter);
}
My test class:
#DataMongoTest()
#RunWith(SpringRunner.class)
public class ParticipantRepositoryTest {
#Autowired
private MongoTemplate mongoTemplate;
#Autowired
private ParticipantRepository repository;
private List<Participant> participants;
#Before
public void setUp() throws Exception {
participants = createParticipants();
repository.saveAll(participants);
}
#After
public void tearDown() throws Exception {
repository.deleteAll();
}
#Test
public void findAllByLoggedInTrueAndExpirationAfter_shouldNotReturnLoggedOutParticipants() {
List<Participant> result = repository.findAllByLoggedInTrueAndAccessTokenExpirationAfter(new Date());
getLoggedOutParticipants().forEach(participant -> assertThat(participant, not(isIn(result))));
}
...
}
I have a spring boot project where I'm trying to mock my repository for tests.
I would like my repository.count() to return 30L but it actually always return 0
#Service
#Transactional
public class DishServiceImpl implements DishService {
private final DishRepository dishRepository;
public DishServiceImpl(DishRepository dishRepository) {
this.dishRepository = dishRepository;
}
#Override
public List<Dish> searchDishes() {
long countDish = dishRepository.count();
System.out.println(countDish);
[...]
}
}
#RunWith(SpringRunner.class)
#SpringBootTest(classes = WhatAreWeEatingApp.class)
#Transactional
public class DishServiceTest{
#Mock
private DishRepository dishRepository;
#Autowired
private DishService dishService;
#Test
public void test(){
when(dishRepository.count()).thenReturn(30L);
dishService.searchDishes();
[...]
}
You repository mock is never set as dependency to the bean service.
Here you mock in the frame of a running Spring container :
#RunWith(SpringRunner.class)
#SpringBootTest(classes = WhatAreWeEatingApp.class)
It is not unit test. So you want to use #MockBean from Spring Boot to mock a bean in the container, not #Mock from Mockito to mock instances created outside the container.
Don't like auto promotional post but this question should help you.
To go further you should not need to run a container to test the service method. So you should probably remove the Spring Boot test annotation and write a real unit test.
At the beginning i want to admit that i am aware of mockito, EasyMock etc existing.
How can i test my database with OWN mock.
#Entity
public class CLASS{
private Long id;
***constructors/getters/setters****
}
-
public interface CLASSrepo extends JpaRepository<CLASS, Long> {
}
-
#Service
public class CLASSservice {
#Autowired
CLASSrepo classRepo;
}
Any options how to create own mock without using Mockito #MockBean #Mock etc and test service with repo mock?
I am using spring caching layer in my application and I have got an issue as part of writing unit tests to test spring cache layer using Mockito.
Please refer the below code for my problem:
Service layer:
public CustomerServiceImpl implements CustomerService {
#Autowired
private CacheManager cacheManager;
#Autowired
private CustomerRepository customerRepository;//Repository is simple JPA repository interface which contains findByCustomerName()
#Override
#CachePut(value = "#customer", key = "#customer.customerName")
public Customer insertOrUpdate(Customer customer) {
return customerRepository.save(customer);
}
#Cacheable(value="customersCache", key = "#customerName")
public Customer findByCustomerName(String customerName) {
Customer customer = customerRepository.findByCustomerName(customerName);
return customer;
}
}
JUnit test Code for Service layer:
#RunWith(PowerMockRunner.class)
#PrepareForTest(CustomerServiceImplTest.class)
public class CustomerServiceImplTest {
#Spy
CacheManager cacheManager = new ConcurrentMapCacheManager("customersCache");
#Mock
CustomerRepository CustomerRepository;
#InjectMocks
CustomerServiceImpl customerServiceImpl = new CustomerServiceImpl();
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testCacheForFindByCustomerName() {
Customer customer1 = new Customer();
customer1.setId("1");
customer1.setName("John");
Customer customer2 = new Customer();
customer2.setId("2");
customer2.setName("Sara");
//this should save to cache
Mockito.when(CustomerRepository.save(customer1))
.thenReturn(customer1);
customerServiceImpl.insertOrUpdate(customer1);
//Now it should retreive from cache, but not able to
Mockito.when(CustomerRepository.findByCustomerName(Mockito.any(String.class)))
.thenReturn(customer1, customer2);
Customer result = customerServiceImpl.findByCustomerName("John");
assertThat(result, is(customer1));
result = customerServiceImpl.findByCustomerName("John");
assertThat(result, is(customer1));
}
}
Exception:
I am getting an "java.lang.AssertionError:" because the caching layer did not work and the call has been passed to repository object (twice) which has returned the 'customer2' mock object above i.e., the repository method has been invoked twice for the same key by passing the service layer.
Also, please note that I am using "Mockito" framework for my tests.
I have tried to google for unit tests on spring caching and also referred the below URL, which is almost using the same concept, but it does not work for my above code.
How to test Spring's declarative caching support on Spring Data repositories?
Could you please help to resolve the above exception ?
The Spring Cache Manager relies on Spring managing the application. You can't get that with the PowerMockRunner, you need to use SpringJUnit4Runner. You can still use PowerMock or Mockito programmatically, but not as a Runner.
Typically, you'll turn your unit test into a Spring-style integration test, something like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class SpringTest {
#Configuration
#EnableCaching
static class SpringConfig{
#Bean
public CustomerService customerService(){
return new CustomerServiceImpl(customerRepository());
}
#Bean
public CustomerRepository customerRepository(){
return Mockito.mock(CustomerRepository.class);
}
}
#Autowired
CustomerService customerService; // this will contain a proper managed cache
#Autowired
CustomerRepository customerRepository; // this is a mockito mock you can fine-tune
}