Spring Testing Unsatisfied depencency NoSuchBeanDefinitionException - spring

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.

Related

Spring Boot Unit testing with request scope bean dependency

I'm trying to run Unit tests on a service class that has an auto wired dependency which is request scoped. When running the unit test, it seems like spring container is unable to find the bean to auto wire into the service class. What should I do to fix this?
Application.java
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
AppConfig.java
#Configuration
public class AppConfig {
#Bean
#Scope("request")
public ClassA classA(){
return new ClassA();
}
}
ServiceA.java
#Service
public class ServiceA {
#Autowired
private ClassA classA;
public void doSomething(){
String value = classA.getValue();
System.out.println(value);
}
}
ServiceTest.java
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ServiceATest {
#Test
void contextLoads() {
}
}
Error message
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'serviceA': Unsatisfied dependency
expressed through field 'classA'; nested exception is
org.springframework.beans.factory.support.ScopeNotActiveException:
Error creating bean with name 'classA': Scope 'request' is not active
for the current thread; consider defining a scoped proxy for this bean
if you intend to refer to it from a singleton; nested exception is
java.lang.IllegalStateException: No thread-bound request found: Are
you referring to request attributes outside of an actual web request,
or processing a request outside of the originally receiving thread? If
you are actually operating within a web request and still receive this
message, your code is probably running outside of DispatcherServlet:
In this case, use RequestContextListener or RequestContextFilter to
expose the current request.

Unit test #WebMvcTest NoSuchBeanDefinitionException

I am writing a test for a very simple standalone Controller:
#CrossOrigin(origins = "*", maxAge = 3600)
#RestController
public class TestController {
#PostMapping("/version")
public String getVersion() {
// String a = TUtil.TestUtil("ds");
String a = "a";
return a;
}
}
This controller only defines and returns a String, it is not dependent on any other classes.
Here is my test:
#RunWith(SpringRunner.class)
#WebMvcTest(TestController.class)
public class TestControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void getVersion() throws Exception {
this.mockMvc.perform(MockMvcRequestBuilders.post("/version"))
.andExpect(MockMvcResultMatchers.status().is4xxClientError())
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.content().string("a"))
.andReturn();
}
}
then it gives a lot of errors, some of them are:
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'ipArcuserauthMapper' defined in file [/Users/xy/Desktop/springweb/aaa/dccb/target/classes/com/dccb/tft/mapper/IpArcuserauthMapper.class]:
Invocation of init method failed;
nested exception is java.lang.IllegalArgumentException: Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required
It says I need to define a bean of type 'IpArcuserauthMapper' which has nothing remotely to do with the TestController.
In another case, if I use the SpringbootTest annotation, the problem can be solved:
#SpringBootTest(classes = {TftApplication.class})
#AutoConfigureMockMvc
#RunWith(SpringRunner.class)
I have no idea what is going on with #WebMvcTest(TestController.class)??
From what I know the WebMvcTest annotation only scans the class defined in the brackets.
Please help !!

SpringBootTest Spring Security InMemoryClientRegistrationRepository NoSuchBeanDefinitionException in Test

I created a custom OAuth2AuthorizationRequestResolver that is initialized with a ClientRegistrationRepository and String authorizationRequestBaseUri. It is based on this implementation here https://www.baeldung.com/spring-security-custom-oauth-requests.
I initialize my custom resolver in my WebSecurityConfig.
#Autowired
private InMemoryClientRegistrationRepository inMemoryClientRegistrationRepository;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
// ...
.oauth2Login(oAuth2LoginConfigurer -> oAuth2LoginConfigurer
.authorizationEndpoint()
.authorizationRequestResolver(new CustomtAuthorizationRequestResolver(inMemoryClientRegistrationRepository, "/oauth2/authorization"))
)
// ...
}
When I run the app, the InMemoryClientRegistrationRepository is correctly autowired and loads my clients from my application.yml.
In my test, I want to have the same InMemoryClientRegistrationRepository with the clients loaded from my application.yml. However, I cannot find a way to inject / autowire the InMemoryClientRegistrationRepository.
#SpringBootTest
#ContextConfiguration(classes = { TestConfiguration.class, InMemoryClientRegistrationRepository.class },
initializers = ConfigFileApplicationContextInitializer.class)
#ActiveProfiles("test")
public class CustomAuthorizationRequestResolverTests {
#Autowired
private InMemoryClientRegistrationRepository inMemoryClientRegistrationRepository;
#Test
public void firstTest() {
HttpServletRequest request = new MockHttpServletRequest();
OAuth2AuthorizationRequestResolver resolver = new CustomAuthorizationRequestResolver(inMemoryClientRegistrationRepository, "/oauth2/authorization");
resolver.resolve(request, "client");
// ...
}
}
With this code I get
Unsatisfied dependency expressed through field 'inMemoryClientRegistrationRepository'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
I can only find tests that use InMemoryClientRegistrationRepository by building it separately in the test and I cannot find any tests that load the clients from the application.yml. What do I have to do so that I can use my InMemoryClientRegistrationRepository with the values from my application.yml in my tests?
Update
I use the default TestConfiguration here and ConfigFileApplicationContextInitializer here classes from spring-boot-test-2.3.1.

Cucumber Spring and class configuration

I'm struggling with Cucumber and Spring configuration.
I'm writing selenium framework using Page Object Pattern, with BrowserFactory.
When I use #ComponentScan, #Component and #Autowire annotations everything works fine, but when I want to create a bit more complicated bean with #Bean annotation (BrowserFactory which registers few browser drivers) in #Configuration class it does not work, during debug I'm getting nulls on every single variable I'm trying to Autowire.
I'm using Spring 4.2.4, all cucumber dependencies in version 1.2.4.
Config:
#Configuration
public class AppConfig {
#Bean
#Scope("cucumber-glue")
public BrowserFactory browserFactory() {
BrowserFactory browserFactory = new BrowserFactory();
browserFactory.registerBrowser(new ChromeBrowser());
browserFactory.registerBrowser(new FirefoxBrowser());
return browserFactory;
}
#Bean(name = "loginPage")
#Scope("cucumber-glue")
public LoginPage loginPage() throws Exception {
return new LoginPage();
}
#Bean(name = "login")
#Scope("cucumber-glue")
public Login login() {
return new Login();
}
}
POP:
public class LoginPage extends Page {
public LoginPage() throws Exception {
super();
}
...
}
Page:
public class Page {
#Autowired
private BrowserFactory browserFactory;
public Page() throws Exception{
...
}
}
Login:
public class Login {
#Autowired
private LoginPage loginPage;
public Login(){}
...
}
Steps:
#ContextConfiguration(classes = {AppConfig.class})
public class LoginSteps {
#Autowired
Login login;
public LoginSteps(){
}
#Given("^an? (admin|user) logs in$")
public void adminLogsIn(Login.User user) throws Exception {
World.currentScenario().write("Logging in as " + user + "\n");
login.as(user);
}
}
Error:
cucumber.runtime.CucumberException: Error creating bean with name 'LoginSteps': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: Login LoginSteps.login; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'login': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private LoginPage Login.loginPage; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'loginPage' defined in AppConfig: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [LoginPage]: Factory method 'loginPage' threw exception; nested exception is java.lang.NullPointerException
And now for the fun part...
BrowserFactory in World class is properly Autowired!!
World:
public class World {
#Autowired
private BrowserFactory browserFactory;
...
}
So I'll answer my own question:)
Issue was that I was calling BrowserFactory inside of Page constructor.
Looks like this bean was not yet created and was causing NPEs.
In order to fix that I:
Added #Lazy annotation to configuration (all elements that use this configuration, both defined in that class and those which will be found by Scan will be created as Lazy)
Moved call to Browser Factory to #PostConstruct method
Two more things to increase readability of Spring config:
Added #ComponentScan to configuration
Classes with no constructor parameters are annotated with #Component and #Scope("cucumber-glue") annotation so bean creation can be removed from AppConfig.class

BeanCreationException on Spring TestNG PowerMock test

I'm getting BeanCreationException when using #AutoWired (Spring Annotation) and #PrepareForTest (PowerMock) and running my Spring enabled TestNG test.
I have a Spring controller, picked up via component scan.
I'm testing with TestNG, Mockito, and PowerMock.
I'm trying to spy on an auto wired controller thats autowired into the test, via: #AutoWired annotation.
Here's the beginning of the test class:
#PowerMockIgnore("*")
#WebAppConfiguration
#ContextConfiguration(locations = { "classpath:applicationContext-test.xml" })
#PrepareForTest(IWantAHamburgerController.class)
public class IWantAHamburgerControllerTest extends AbstractTestNGSpringContextTests {
#Autowired
private WebApplicationContext wac;
#Autowired
private MockHttpSession session;
#Autowired
private MockHttpServletRequest request;
#Autowired
private IWantAHamburgerController iWantAHamburgerController;
private MockMvc mockMvc;
#ObjectFactory
public IObjectFactory getObjectFactory() {
return new org.powermock.modules.testng.PowerMockObjectFactory();
}
#BeforeClass
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
// see the note at the bottom of this post about this line
//iWantAHamburgerController = (IWantAHamburgerController) applicationContext.getBean("iWantAHamburgerController");
}
I'm trying to test a GET method on IWantAHamburgerController. This is what the test looks like:
#Test
public void testGetHamburgerAfterAskingThisQuestion() throws Exception {
Principal p = PowerMockito.mock(Principal.class);
PowerMockito.when(p.getName()).thenReturn("oneofthefiveguys");
IWantAHamburgerController spy = PowerMockito.spy(iWantAHamburgerController);
PowerMockito.doReturn("oneofthefiveguys").when(spy).getUserName("oneofthefiveguys");
mockMvc.perform(get("/hamburgers/everythinghamburger.html")).andExpect(status().isOk())
.andExpect(view().name("jsp/hamburger/everythinghamburger"))
.andExpect(forwardedUrl("jsp/hamburger/everythinghamburger"));
PowerMockito.verifyPrivate(spy).invoke("initGrill", "oneofthefiveguys");
new org.mockito.internal.debugging.MockitoDebuggerImpl().printInvocations(spy);
}
Inside the test I want to spy on the autowired iWantAHamburgerController in order to verify that initGrill was called by the GET method on the controller.
If I remove #PrepareForTest(IWantAHamburgerController.class) I do not get a BeanCreationException, but then PowerMock doesn't work.
Note: I've tried to manually set the iWantAHamburgerController bean, but when I do, I get a ClassCastException.
Here's the full stack:
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name
'com.fiveguys.controllers.IWantAHamburgerControllerTest': Injection of
autowired dependencies failed; nested exception is
org.springframework.beans.factory.BeanCreationException: Could not
autowire field: private
com.fiveguys.controllers.IWantAHamburgerController
com.fiveguys.controllers.IWantAHamburgerControllerTest.iWantAHamburgerController;
nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type
[com.fiveguys.controllers.IWantAHamburgerController] found for
dependency: expected

Resources