Beans in Spring test - spring

I have one user controller.
User controller:
#CrossOrigin(origins = "*", allowedHeaders = "*")
#RestController
#RequestMapping("users")
public class UserRestController {
private final UserService userService;
public UserRestController(final UserService userService) {
this.userService = userService;
}
#GetMapping()
public User getInfo(
final #AuthenticationPrincipal UserDetails userDetails
) {
return userService.findByUsername(userDetails.getUsername());
}
#PutMapping()
public User update(
final #AuthenticationPrincipal UserDetails userDetails,
final #RequestBody User user
) {
User userInSystem = userService.findByUsername(
userDetails.getUsername()
);
return userService.update(user, userInSystem);
}
}
I make some test for him.
Test User controller:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = ApplicationArguments.class)
#Configuration // this annotation can be removed, the error will remain the same
#EnableAutoConfiguration //previous comment
public class TestUserRestController {
#Autowired
private UserRestController userRestController;
#Test
public void Test() {
assertThat(userRestController).isNotNull();
}
}
And in the end, I have an error when running the test:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.daniil.ostrouh.notes.TestUserRestController': Unsatisfied dependency expressed through field 'userRestController'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.daniil.ostrouh.notes.rest.UserRestController' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
Please tell me what I'm doing wrong
Updates
Project Structure: Project Structure

Every standard Spring Boot application should have a production class which is annotated with the #SpringBootApplication annotation. This annotation does a lot including signaling Spring about the root of the component scan. Component scan is used for auto-detecting Spring beans (#Component, #RestController, etc). The #SpringBootTest tries to auto-detect that class is the classes if left empty. If the classes has any value then the Spring context will contain only the specified classes (in this case The ApplicationArguments which does nothing here).
So you should remove the classes from #SpringBootTest and your packages should be structured in hierarchy as the component scan uses this hierarchy.
If you need more help please post your project.

Related

How to use unit of work in rest controller properly?

public interface CourseRepo extends CrudRepository<Course, Long> {
}
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public class UnitOfWork {
CourseRepo courses;
StudentRepository students;
StudyProgramRepository studyPrograms;
StudySchemeRepo studySchemes;
FeeStructureRepository feeStructures;
}
#RestController
public class TestController {
#Autowired
UnitOfWork uow;
#GetMapping("/addcr")
public String addCourse() {
Course cr = new Course();
cr.setTitle("DingDong course");
uow.getCourses().save(cr);
return "course Added..!!" ;
}
APPLICATION FAILED TO START
***************************
Description:
Field uow in com.srs.TestController required a bean of type 'com.srs.uow.UnitOfWork' 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.srs.uow.UnitOfWork' in your configuration.
if i remove autowired and add a bean
#RestController
public class TestController {
#Bean
public UnitOfWork uow() {
return new UnitOfWork();
}
#GetMapping("/addcr")
public String addCourse() {
Course cr = new Course();
cr.setTitle("DingDong course");
uow().getCourses().save(cr);
return "course Added..!!" ;
}
java.lang.NullPointerException: Cannot invoke "com.srs.jpa.CourseRepo.save(Object)"
because the return value of "com.srs.uow.UnitOfWork.getCourses()" is null
i tried both autowired and in this case how can i use autowired or bean properly ?
Your class need to be annotated with #Component to be used with DI provider by #Autowired annotation
For the same reason each repository of your class need to be annotated with #Autowired
The Error Message gives the answer.
Field uow in com.srs.TestController required a bean of type 'com.srs.uow.UnitOfWork' that could not be found.
spring is searching for a bean from type UnitOfWork. You have to add this class to the application context from spring boot. To accomplish this you have to annotate the class UnitOfWork with #bean or #Data if you use lombok.
After this the spring application can find the Class UnitOfWork and auto wire it.
Since UnitOfWork (a somewhat misleading name in the JPA context) autowires data repositories, it has to be a Spring Bean itself.
The easiest and most common way is to annotate the class with one of the annotations #Service, #Component or #Bean, depending on the semantic of the class. There are also other ways, like the #Bean on method-level as you used.
To use the fully initialized bean you need to autowire it where you want to use it, not calling the create method. E.g. calling uow() as in your sample, bypasses the Spring Bean mechanism and creates a new instance, which hasn't been fully initialized (thus the NullPointerException).
Usually, the beans are autowired as fields, sometimes they are autowired in mehtod parameters (especially when working with #Bean on method-level in the same class).
E.g.
#Component
#Getter
#RequiredArgsConstructor
public class UnitOfWork {
private final CourseRepo courses;
private final StudentRepository students;
private final StudyProgramRepository studyPrograms;
private final StudySchemeRepo studySchemes;
private final FeeStructureRepository feeStructures;
}

Spring Testing Unsatisfied depencency NoSuchBeanDefinitionException

When I try to run my tests they all fail becaouse they can't find the bean of one of my classes.
Here are my codes which are used in the context:
The exception I get is this:
Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'testProtoAdminController' : Unsatisfied dependency expressed through constructor parameter 2; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'TestProtoCopyService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
TestProtoAdminControllerTest
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = TestProtoAdminController.class)
public class TestProtoAdminControllerTest {
//Some used services
#Before
public void setUp() {
authenticatedMockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
#WithMockUser
public void testCopyProto() throws Exception {
authenticatedMockMvc.perform(post("/api/admin/{id}/copy", 1)
.contentType(MediaType.APPLICATION_JSON)
.content(asJson(new TestProtoBaseVo()))).andExpect(status().isOk());
}
//Some more tests which are not important in this case
TestProtoCopyService
#Service
public class TestProtoCopyServiceImpl implements TestProtoCopyService {
//Other services and repositories I have to use.
//Methods
}
TestProtoCopyService
public interface TestProtoCopyService {
#Transactional
void copyTestProto(long testProtoId, String sourceTenant, String targetTenant);
}
TestProtoAdminController
#RestController
#RequestMapping("/*")
public class TestProtoAdminController {
private TestProtoCopyService testProtoCopyService;
public TestProtoAdminController(TestProtoCopyService testProtoCopyService {
this.testProtoCopyService = testProtoCopyService;
}
When using #WebMvcTest Spring will prepare everything to test your web layer. This doesn't mean all your beans are scanned and are part of this test application context and ready to inject.
In general, you usually mock the service class of your controller with #MockBean and then use Mockito to specify its behavoir:
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = TestProtoAdminController.class)
public class TestProtoAdminControllerTest {
#MockBean
private TestProtoCopyService mockedService
// the rest
#Before
public void setUp() {
authenticatedMockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
#WithMockUser
public void testCopyProto() throws Exception {
authenticatedMockMvc.perform(post("/api/admin/{id}/copy", 1)
.contentType(MediaType.APPLICATION_JSON)
.content(asJson(new TestProtoBaseVo()))).andExpect(status().isOk());
}
If you want Spring Boot to bootstrap the whole application context with every bean consider using #SpringBootTest. With this annotation, you can inject any bean to your application. The downside here is that you need to provide the whole infrastructure (database/queues/etc.) for your test.

Is there a way to include a spring component in a WebMvcTest

Given production code classes:
#RestController
#RequiredArgsConstructor
public class MyController {
private final MyValidator validator;
// annotations relating to request mapping excluded for brevity
public void test(#Valid #RequestBody final MyParams params) {
// do stuff
}
#InitBinder
#SuppressWarnings("unused")
protected void initBinder(final WebDataBinder binder) {
binder.setValidator(validator);
}
}
and
#Component
#RequiredArgsConstructor
public class MyValidator implements Validator {
...
#Override
public void validate(final Object target, final Errors errors) {
// custom validation
}
}
and finally test code:
#RunWith(SpringRunner.class)
#WebMvcTest(MyController.class)
public class MyControllerTest {
// tests
}
I encounter the error:
NoSuchBeanDefinitionException: No qualifying bean of type 'MyValidator' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
I think the error is fair enough. I've annotated the test as a WebMvcTest, which I believe has excluded #Component beans. This is intentional and desired (from the perspective that I am only wanting to test the "web layer", not the whole context - it just so happens I need a component which is related/used only in the controllers)
My question, therefore, is: how can one explicitly include a component like a validator in the test context for a web test?
My environment is java version "10.0.2" 2018-07-17, spring boot 1.5.16.RELEASE.
There are two ways to solve this.
Using #SpringBootTest and #AutoConfigureMvc instead of #RunWith(SpringRunner.class) and #WebMvcTest.
#SpringBootTest
#AutoConfigureMvc
public class MyControllerTest {
}
Creating a #TestConfiguration class that injects the 'MyValidator' bean as:
#RunWith(SpringRunner.class)
#WebMvcTest(MyController.class)
public class MyControllerTest {
#TestConfiguration
static class TestConfig {
#Bean
MyValidator getMyValidator(){
return new MyValidator();
}
}
// tests
}
More on this can be found here : https://mkyong.com/spring-boot/spring-boot-how-to-init-a-bean-for-testing/
There are two ways to test the web layer
first.
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyControllerTest {
#Autowired
private MyController myController;
}
The #SpringBootTest annotation tells Spring Boot to go and look for a
main configuration class (one with #SpringBootApplication for
instance), and use that to start a Spring application context.
A nice feature of the Spring Test support is that the application
context is cached in between tests, so if you have multiple methods in
a test case, or multiple test cases with the same configuration, they
only incur the cost of starting the application once. You can control
the cache using the #DirtiesContext annotation.
Secondly, if you want to use the #WebMvcTest(MyController.class)
#RunWith(SpringRunner.class)
#WebMvcTest(MyController.class)
public class MyControllerTest {
#MockBean
private MyValidator validator;
}
But this validator is a fake, so you have to customize it for testing.
See this link for more details https://spring.io/guides/gs/testing-web/
I cannot recommend it as a standard practice but if you do need an instance of a dependency in your Web MVC tests (for example in legacy code) you can add them into the spring context using #SpyBean annotation.
Real methods of that class will be called during the test and you can verify them if needed similarly to the beans annotated with #MockBean
#RunWith(SpringRunner.class)
#WebMvcTest(MyController.class)
public class MyControllerTest {
#SpyBean
private MyValidator validator
}

Fail to autowire dependency in test

I'd like to write a Spring integration test to make sure that #Autowired correctly puts together my classes, but fail.
The test class
#RunWith(SpringRunner.class)
#DataJpaTest
#EntityScan(basePackageClasses = {ClassUnderTest.class, SomRepository.class, SomeEntity.class})
public class ClassUnderTestIT{
#Autowired private InterfaceUnderTest cut;
#Test public void autowires() {
assertThat(cut).isNotNull();
}
}
Should test that this #Service autowires
#Service
#Transactional
public class ClassUnderTest implements InterfaceUnderTest {
private final SomeRepository repository;
#Autowired
public DefaultWatchlistDataModifier(SomeRepository repository) {
this.repository = repository;
}
}
Paying special attention to the wired dependency
#Repository
#Transactional(readOnly = true)
#Scope(value = BeanDefinition.SCOPE_PROTOTYPE)
public interface SomeRepository extends JpaRepository<SomeEntity, String> {
}
However, all I ever get is the exception
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type 'com.[...].InterfaceUnderTest' available:
expected at least 1 bean which qualifies as autowire candidate.
Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
Meanwhile, I have experimented with all kinds of additional annotations, such as #ComponentScan, #EnableJpaRepositories ... whatever I could dig up in the endless number of StackOverflow questions on the topic - all in vain.
Using [#DataJpaTest] will only bootstrap the JPA part of your Spring Boot application. As the unit of your test isn't part of that subset it will not be available in the application context.
Either construct it yourself and inject the dependencies or use a full blown #SpringBootTest instead.

Spring annotation based bean injection

So this is basically what I am trying to achieve: Inject User with constructor into UserClass. But it is throwing "No default constructor found" error. As I suspect if I add #Autowired to class User constructor it expects injection there so I'm not really sure where the problem is.
The question might be too basic so you can redirect me to older such questions. There is very little information on annotation based DI.
#Component
public class UserClass {
public User user;
#Autowired
public UserClass(User user) {
this.user = user;
}
}
#Configuration
public class DIconfig {
#Bean
public User getUser() {
return new User('John');
}
}
#Component
public class User {
public String name;
//#Autowired
public User(String name) {
this.name = name;
}
}
Thank you for your time.
You define two beans of the class User, one with #Component, and one with #Bean. The bean configuration with #Bean is fine so far, however the bean definition with #Component is indeed lacking the default constructor. Every bean which is defined with #Component must either have a default constructor or a constructor where all dependencies are autowired. Neither is the case with your bean. So either add a default constructor or remove the #Component and only create beans of that class with an #Bean method.

Resources