junit testing service with injected repository - java

i have a service i need to test, it has injected repository
#Service
public class MyService {
#Inject
private MyRepo myRepo;
public someFunctionToTest(){
// body...
}
}
however i can not figure out how to write a proper junit tests for service
i have tryed following:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { Application.class })
#Configuration
public class NumberGeneratorServiceTest {
#Inject
private MyService myService;
#Inject
private MyRepository MyRepository;
#Test
public void test() {
//do something
}
}
in this case im getting error: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'getMyRepository': Requested bean is currently in creation: Is there an unresolvable circular reference?
what is the proper way to test Services with injections?
EDIT: MyRepository
#Repository
#AllArgsConstructor
#NoArgsConstructor
public class MyRepositoryImpl implements MyRepository {
#Inject
private MyJPARepository entityRepository;
//some methods
}

Related

Spring: Service Class with #Service annotation gets "required bean of type 'abcService' could not be found"

I have a spring batch application in which the writer has an #Autowired field (it is a service class). When running tests for the writer step, I am met with the error:
Field batchTrackingService in com.ally.cr.miscinfo.batch.writer.AdvantageClientItemWriter required a bean of type 'com.test.miscinfo.service.TestService' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.test.miscinfo.service.batchTrackingService ' in your configuration.
I've looked at a few answers to related questions, and most of them are caused by the fact that the class being injected has not been annotated with #Component, #Service, #Repository, etc. However mine is. I also read questions where the supposed solution was to add the #ComponentScan() annotation to the Main class of my application. After trying this, it gave the same error. Can someone please help me? Any help is appreciated.
Here are the relevant classes:
Main class:
#SpringBootApplication
#EnableBatchProcessing
#EnableJpaRepositories("com.test.miscinfo.repository")
#EntityScan("com.test.miscinfo.entity")
#ComponentScan("com.test.miscinfo.service")
public class MiscInfoServiceApplication {
public static void main(String[] args) {
SpringApplication.run(MiscInfoServiceApplication.class, args);
}
}
Writer class:
#Slf4j
#Component
#AllArgsConstructor
#NoArgsConstructor
public class AdvantageClientItemWriter implements ItemWriter<MiscInfo> {
#Autowired private AdvantageClientConfig advantageClientConfig;
#Autowired WebClient advantageClientWebClient;
#Autowired private BatchTrackingService batchTrackingService;
#Override
public void write(List<? extends MiscInfo> miscInfos) throws Exception {
/* some call to a method in the injected service */
}
}
Service class:
#AllArgsConstructor
#NoArgsConstructor
#Slf4j
#Transactional
#Service
public class BatchTrackingService {
public void someMethod() {}
}
Please let me know if I am missing relevant info.
EDIT:
Adding test method:
#ExtendWith(SpringExtension.class)
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#EnableConfigurationProperties(value = AdvantageClientConfig.class)
#SpringBootTest
#ActiveProfiles("test")
#ContextConfiguration(classes = { AdvantageClientItemWriter.class })
public class AdvantageClientItemWriterTest {
#MockBean RestTemplate advantageClientRestTemplate;
#MockBean WebClient advantageWebClient;
WebClient.RequestBodyUriSpec requestBodyUriSpec = mock(WebClient.RequestBodyUriSpec.class);
WebClient.RequestBodySpec requestBodySpec = mock(WebClient.RequestBodySpec.class);
WebClient.ResponseSpec responseSpec = mock(WebClient.ResponseSpec.class);
WebClient.RequestHeadersSpec requestHeadersSpec = mock(WebClient.RequestHeadersSpec.class);
#Autowired AdvantageClientConfig advantageClientConfig;
#Autowired AdvantageClientItemWriter advantageClientItemWriter;
ArgumentCaptor<String> uriCaptor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<MediaType> mediaTypeCaptor= ArgumentCaptor.forClass(MediaType.class);
ArgumentCaptor<String> headerNameCaptor= ArgumentCaptor.forClass(String.class);
ArgumentCaptor<String> headerValueCaptor= ArgumentCaptor.forClass(String.class);
ArgumentCaptor<String> bodyCaptor= ArgumentCaptor.forClass(String.class);
private MemoryAppender memoryAppender;
#BeforeEach
public void init(){
Logger logger = (Logger) LoggerFactory.getLogger("com.test");
memoryAppender = new MemoryAppender();
memoryAppender.setContext((LoggerContext) LoggerFactory.getILoggerFactory());
logger.setLevel(Level.DEBUG);
logger.addAppender(memoryAppender);
memoryAppender.start();
}
#Test
public void successfulAdvantageClientWrite() throws Exception {
setupMockReturns();
when(responseSpec.toBodilessEntity()).thenReturn(Mono.just(new ResponseEntity(null, HttpStatus.OK)));
List<MiscInfo> miscInfos = new ArrayList<>();
final MiscInfo miscInfo = createMiscInfo1();
miscInfos.add(miscInfo);
advantageClientItemWriter.write(miscInfos);
Assertions.assertEquals(advantageClientConfig.getEndpoint(), uriCaptor.getValue());
Assertions.assertEquals(MediaType.APPLICATION_JSON, mediaTypeCaptor.getValue());
Assertions.assertEquals( advantageClientConfig.getHeaderName(), headerNameCaptor.getValue());
Assertions.assertEquals(advantageClientConfig.getApiKey(), headerValueCaptor.getValue());
Assertions.assertEquals(new ObjectMapper().writer().withDefaultPrettyPrinter().writeValueAsString(miscInfos), bodyCaptor.getValue());
assertThat(memoryAppender.search("Write to Advantage status: ", Level.DEBUG).size()).isEqualTo(1);
}
}
This error means that Spring is trying to autowire of bean of type BatchTrackingService in your AdvantageClientItemWriter but it could not find one in the application context. In other words, your test context does not contain a bean definition of type BatchTrackingService, which could be due to one of the following causes:
Either the configuration class that defines that bean is not imported in the test class (in #ContextConfiguration(classes = { AdvantageClientItemWriter.class })
or the class of that bean is not in the package that is scanned by Spring (Boot).
Make sure that:
the class public class BatchTrackingService {} is defined in the package referenced in #ComponentScan("com.test.miscinfo.service")
the #ContextConfiguration annotation imports the class of that bean (something like #ContextConfiguration(classes = { AdvantageClientItemWriter.class, BatchTrackingService.class }), or that it imports a configuration class which defines an instance of that bean.

Spring #WebMvcTest with Spring Security

I have a problem when I want to test controllers using #WebMvcTest - on application load I get:
...
Field customUserDetailsService in com.test.config.SecurityConfig required a bean of type 'com.test.security.CustomUserDetailsService' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.test.security.CustomUserDetailsService' in your configuration.
...
Do you have any idea what is problem? Is it good solution to exclude security?
There are classes:
#RestController
#AllArgsConstructor
public class EmployeeController {
private final EmployeeService employeeService;
#GetMapping
public List<EmployeeDTO> getEmployees() {
return employeeService.getAllEmployees();
}
}
#WebMvcTest(controllers = EmployeeController.class)
class EmployeeControllerTest {
#Autowired
private MockMvc mockMvc;
#InjectMocks
private EmployeeController employeeController;
#Test
void getEmployees_Success() throws Exception {
// mockMvc.perform(get("/employee/all")).andExpect(status().isOk());
}
The controller which you are using in test have a dependency EmployeeService employeeService;
So in test case add below.
#Mock
EmployeeService employeeService;

Can't Autowire Service in JUnit 4 Test in Spring Boot Multi Module Application

I have following project structure:
-module1
--src/main/java/at.flobau.demo.module1
---model
----Product.java
---service
----ProductService.java
---TestConfiguration.java
--src/test/java/at.flobau.demo.module1.service
---ProductServiceTest.java
-module2
--src/main/java/at.flobau.demo.main
---MainApplication.java
The Application class looks like this:
#SpringBootApplication(scanBasePackages = {"at.flobau.demo.main"})
#PropertySource(value = "classpath:application.properties")
#EnableJpaRepositories("at.flobau.demo.module1")
#EntityScan(basePackages = {"at.flobau.demo.module1"})
public class PocApplication {
public static void main(String[] args) {
SpringApplication.run(PocApplication.class, args);
}
}
The Service looks like this:
#Service
public class ProductService implements IProductService {
#Autowired
private IProductRepository productRepository;
...
}
The test Class looks like this:
#SpringBootTest
#ContextConfiguration(classes = { TestConfiguration.class }, loader =
AnnotationConfigContextLoader.class)
#RunWith(SpringRunner.class)
public class ProductServiceTest {
#Autowired
private ProductService productService;
...
}
The test configuration file looks like this:
#Configuration
#ComponentScan("at.flobau.demo")
public class TestConfiguration{ }
IntelliJ tells me, that the ProductService inside the test cannot be autowired. When i do run the test i get an exepction:
Error creating bean with name 'ProductServiceTest': Unsatisfied dependency expressed through field
'productService'; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type
'at.flobau.demo.module1.products.service.ProductService' available: expected at least 1 bean which
qualifies as autowire candidate. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
You should avoid using field injection (even though is possible) and use constructor injection. This will solve this problem as you will be able to pass the service from the constructor but it will also be useful in the future as you can locate usages and trace objects in your code way better than field injections which are "hidden"
So I recommend instead of trying to solve your problem here to refactor your class in constructor injection and pass the service from there either by directly creating the object in your test or by creating a configuration for your test that will generate the object and give the arguments it requires
something like
#ContextConfiguration(classes = { GeneralTester.TestConfig.class })
#RunWith(SpringRunner.class)
public class GeneralTester {
#TestConfiguration
public static class TestConfig {
#Bean
public IProductService productService(final IProductRepository productRepository){
return new ProductService(productRepository);
}
#Bean
public IProductRepository productRepository(){
return mock(IProductRepository.class);
}
}
#Autowire
public IProductService productService;
#Autowire
public IProductRepository productRepository;
#Before
public void setUp() {
reset(productRepository);
}
#After
public void tearDown() {
verifyNoMoreInteractions(productRepository);
}
#Test
public void doSmth() {
//... your setup
when(productRepository.save(any())).thenReturn("something");
//... your call and assertions
verify(productRepository).save(any());
}
}
You can annotate your test class with #SpringBootTest(classes = ProductService.class)
Have you tried creating a bean of the service in the test configuration class?
#TestConfiguration
#ComponentScan("at.flobau.demo")
public class TestConfiguration {
#Bean
public ProductService productService() {
return new ProductService();
}
}

Beans are not Autowired during tests (java.lang.NullPointerException)

my application normally works fine, but when I run tests, or build application by maven, application is shutting down due tests with errors java.lang.NullPointerException. I debugged it and find out my that my beans in service layer are not Autowired and they are null.
Here is my class with tests:
public class CompanyServiceSimpleTest {
private CompanyService companyService;
#Before
public void setUp() {
companyService = new CompanyServiceImpl();
}
// Here is sample test
#Test
public void testNumberOfCompanies() {
Assert.assertEquals(2, companyService.findAll().size());
}
}
companyService is initialized, but beans in it not. Here is CompanyServiceImpl:
#Service
public class CompanyServiceImpl implements CompanyService {
#Autowired
private CompanyRepository companyRepository; // is null
#Autowired
private NotificationService notificationService; // is null
#Override
public List<CompanyDto> findAll() {
List<CompanyEntity> entities = companyRepository.find(0, Integer.MAX_VALUE);
return entities.stream().map(Translations.COMPANY_DOMAIN_TO_DTO).collect(Collectors.toList());
}
// ... some other functions
}
So when is called companyRepository.find() applications crashes. Here is repository class:
#Repository
#Profile("inMemory")
public class CompanyInMemoryRepository implements CompanyRepository {
private final List<CompanyEntity> DATA = new ArrayList<>();
private AtomicLong idGenerator = new AtomicLong(3);
#Override
public List<CompanyEntity> find(int offset, int limit) {
return DATA.subList(offset, Math.min(offset+limit, DATA.size()));
}
// ... some others functions
}
I have set up profile for that service but I had that VM options in Idea:
-Dspring.profiles.active=develpment,inMemory
So it should works.
To make autowiring work it has to be a Spring integration test. You have to anotate your test class with:
#RunWith(SpringJUnit4ClassRunner.class) and #ContextConfiguration(classes = {MyApplicationConfig.class})
If it is a Spring Boot app e.g.:
#RunWith(SpringJUnit4ClassRunner.class) and #SpringBootTest(classes = {MyApp.class, MyApplicationConfig.class}, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
More on this topic: http://www.baeldung.com/integration-testing-in-spring and http://www.baeldung.com/spring-boot-testing
You are not configuring Spring in your TestClasses, so it's impossible to inject anything...
Try configurin your class with
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "path to your config xml" })
A little example:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:config/applicationContext.xml"})
public class MyTest {
#Autowired
private MyClass myInjectedClass;
#Test
public void someTest() {
assertNotNull(myInjectedClass);
}
}

How to create a bean of repository class

I'm doing unit test using spring mvc test framework.
The following is my source code:
com.exmple.main
MyController.java
#Controller
public class MyController {
#Autowired
private MyService myService;
#RequestMapping(method = RequestMethod.POST)
#ResponseBody
public Map<Object, Object> myControllerFunction(#RequestBody final Object jsonRequest) {
/* do something */
return response;
}
}
MyRepository.java
#Repository
public interface MyRepository extends JpaRepository<My, String> {
#Query(value="select * from my d where (d.start_date<to_date(:date,'YYYY/DD/MM')) and (d.end_date>to_date(:date,'YYYY/DD/MM'))", nativeQuery=true)
List<My> findByDate(#Param("date") String date);
}
MyService.java
public interface MyService {
List<My> findByDate(String date);
}
MyServiceImpl.java
#Service
public class MyServiceImpl implements MyService {
#Autowired
MyRepository destRepo;
#Override
public List<My> findByDate(String date) {
List<My> listDest = destRepo.findByDate(date);
return listDest;
}
}
com.example.test
MyControllerTest.java
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes={TestConfig.class})
#WebAppConfiguration
public class MyControllerTest {
private MockMvc mockMvc;
#Autowired
MyService myService;
#Autowired
protected WebApplicationContext webApplicationContext;
#Before
public void setup() throws Exception {
// this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
public void listAllMy() throws Exception {
}
}
TestConfig.java
#Configuration
public class TestConfig {
#Bean
public MyService myService() {
// set properties, etc.
return new MyServiceImpl();
}
}
When I run test, the following error is displayed
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException
I know the exception occurred because MyService didn't find any bean of MyRepository.
But I don't know how to create a bean of repository.
Please teach me how to create a bean of repository class using Java (not xml).
You need to enable the JPA repositories in your config class, specify the package that contains the repositories as below
#Configuration
#EnableJpaRepositories(basePackages = {
"com.example.repository"
})
public class TestConfig {
#Bean
public MyService myService() {
// set properties, etc.
return new DestinationServiceImpl();
}
}
Edit: looks like you haven't defined entityManager, and dataSource. Refer to a tutorial here and also answer to similar question here

Categories

Resources