I have a unit test that is testing a RestController using #WebMvcTest. The the controller class autowires a service class that I would like to mock. I found that I can use #Profile and #Configuration to create a config class for specifying primary beans to use when a profile is active. I tried adding an active profile to my unit test class, but it says it failed to load the ApplicationContext. I'm not sure how I can do that while using #WebMvcTest.
#ActiveProfiles("mockServices")
#RunWith(SpringRunner.class)
#WebMvcTest(VoteController.class)
public class VoteControllerTest {
...
It seems I may be approaching this wrong. Any help is appreciated.
Edit:
Here is my configuration class:
#Profile("mockService")
#Configuration
public class NotificationServiceTestConfiguration {
#Bean
#Primary
public VotingService getVotingService() {
return Mockito.mock(VotingService.class);
}
}
The error I'm actually getting is:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'VotingService'
I was able to solve it by using #MockBean for VotingService in my Unit Test class. However, I want to use the Profile configuration I have in NotificationServiceTestConfiguration without having to call out mock beans.
Any thoughts?
Related
I'm struggling with configuration for my #DataJpaTest. I'd like to take advantage of auto-configured spring context provided by #DataJpaTest, but I'd like to override some of it's beans.
This is my main class:
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Bean
public CommandLineRunner commandLineRunner(BookInputPort bookInputPort) {
return args -> {
bookInputPort.addNewBook(new BookDto("ABC", "DEF"));
bookInputPort.addNewBook(new BookDto("GHI", "JKL"));
bookInputPort.addNewBook(new BookDto("MNO", "PRS"));
};
}
As you can clearly see, I provide my implementation for CommandLineRunner that depends on some service.
I also have a test:
#DataJpaTest
public class BookRepositoryTest {
public static final String TITLE = "For whom the bell tolls";
public static final String AUTHOR = "Hemingway";
#Autowired
private BookRepository bookRepository;
#Test
public void testRepository() {
Book save = bookRepository.save(new Book(TITLE, AUTHOR));
assertEquals(TITLE, save.getTitle());
assertEquals(AUTHOR, save.getAuthor());
}
}
When I run the test I get the following error:
No qualifying bean of type 'com.example.demo.domain.book.ports.BookInputPort' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
That makes perfect sense! Auto-configured test provides implementation only for a 'slice' of the context. Apparently implementation of BookInputPort is missing. I do not need this commandLineRunner in a test context. I get create a commandLineRunner that does not depend on any service.
I can try to solve the issue by adding to my test class nested class :
#TestConfiguration
static class BookRepositoryTestConfiguration {
#Bean
CommandLineRunner commandLineRunner() {
return args -> {
};
}
}
That solves the problem. Kind of. If I had more tests like this I would have to copy-paste this nested class to each test class. It's not an optimal solution.
I tried to externalize this to a configuration that could be imported by #Import
This is the config class:
#Configuration
public class MyTestConfiguration {
#Bean
public CommandLineRunner commandLineRunner() {
return args -> {
};
}
}
But then the application fails with a message:
Invalid bean definition with name 'commandLineRunner' defined in com.example.demo.DemoApplication: Cannot register bean definition
I checked that error and other folks on SO suggested in this case:
#DataJpaTest(properties = "spring.main.allow-bean-definition-overriding=true")
I did that and I got:
No qualifying bean of type 'com.example.demo.domain.book.ports.BookInputPort' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
That is exactly the same problem I started with.
I took all these steps and found myself in the place where I was at the very beginning.
Do you have any idea how to solve this issue? I do not have a vague idea or clue.
#DataJpaTest generally starts scanning from current package of the test class and scans upwards till it finds the class annotated with #SpringBootConfiguration. So creating a SpringBootConfiguration class at the repository root package will create only beans defined within that package. On top of that we can add any customized test bean required for our test class in that configuration class.
#SpringBootConfiguration
#EnableAutoConfiguration
public class TestRepositoryConfig {
#Bean
public CommandLineRunner commandLineRunner() {
return args -> {
};
}
#Bean
public BookInputPort bootInputPort(){
return new BookInputPort();
}
}
In Spring Boot test class you can do it like this
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class YourClassTest {
#Autowired
private YourService yourService;
It does load all the services and I am using it perfectly fine
How to load specific entities in one environment and not the other
You can use #Profile annotation in your entities.
Put #Profile({"prod"}) on entities/services you don't want to load in test run, and
Put #Profile({"prod", "test"}) on entities/services you want to load in both test and prod environments
Then run the test with test profile. It will not load unnecessary entities.
You can put #Profile annotation on other services too.
The annotation #DataJpaTest only loads the JPA part of a Spring Boot application, the application context might not loaded because you have #DataJpaTest annotation. Try to replace the #DataJpaTest with #SpringBootTest.
As per Spring documentation:
By default, tests annotated with #DataJpaTest will use an embedded
in-memory database (replacing any explicit or usually auto-configured
DataSource). The #AutoConfigureTestDatabase annotation can be used to
override these settings. If you are looking to load your full
application configuration, but use an embedded database, you should
consider #SpringBootTest combined with #AutoConfigureTestDatabase
rather than this annotation.
i have a config class in a config package that looks like this:
package com.x.y.appName.config
#ComponentScan(basePackageClasses = { Application.class })
#Configuration
public class AppConfig {
my project is failing on build within SomeClass that uses the AppConfig bean, saying:
Error creating bean with name 'someClass': Unsatisfied dependency
expressed through field 'appConfig'; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type
But when I stdout print the list of beans Spring is aware of, it lists appConfig there
SomeClass is also in the config package, and looks like this:
package com.x.y.appName.config;
#Configuration
public class SomeClass implements WebMvcConfigurer {
#Autowired
AppConfig appConfig;
but if i add this to SomeClass, it builds fine and all tests pass:
#ComponentScan("com.x.y.appName.config")
in the past ive never needed to ComponentScan the same package that another bean is also declared in
again to clarify, i can bootRun the app just fine, but this spring error is throwing during build or test. do i need to add something to the unit tests? I dont have unit tests for either of the above classes as they would be too frivolous. So what could be going on? Do I need to annotate other unit tests somewhere?
I have this test class :
#RunWith(SpringRunner.class)
#WebMvcTest(ClassToBeTested.class)
public class ClassToBeTestedTests {
#Autowired
private MockMvc mockMvc;
#Test
public void simpleTestMethodToGetClassWorking(){
Assert.assertTrue(true);
}
}
but in the class I want to test, I have this line :
#Autowired
AnnoyingServiceWhichIsADependency annoyingDependency;
So when I try to run the test class - I get this error :
java.lang.IllegalStateException: Failed to load ApplicationContext
and the cause by line seems to throw this up :
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'ClassToBeTested': Unsatisfied dependency expressed through field 'AnnoyingServiceWhichIsADependency'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type '<package-path>.AnnoyingServiceWhichIsADependency' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
I will add that the actual class does work, and does what it is meant to do, but I am having trouble making it work in the unit test world.
All help appreciated.
The reason a bean is not created for the dependency class is that you're using #WebMvcTest and not #SpringBootTest: only controllers and the MVC infrastructure classes are scanned. From the docs:
Can be used when a test focuses only on Spring MVC components.
Since it's an MVC test, you can mock the service dependency.
Example: https://reflectoring.io/spring-boot-web-controller-test/
#WebMvcTest is only going to scan the web layer- the MVC infrastructure and #Controller classes. That's it. So if your controller has some dependency to other beans from, e.g. form your service layer, they won't be found to be injected.
If you want a more comprehensive integration test, use #SpringBootTest instead of #WebMvcTest
If you want something closer to a unit test, mock your dependency.
Also note that Field injection (#Autowired directly on the field) is not recommended exactly for these reasons. I recommend you change to constructor injeciton ( add a constructor for Classtobetested and place the #Autowired annotation on it. ) Then for a unit test you can pass in a mock. Constructor injection leads to a more testable and configurable design.
Your test application context is trying to load your ClassToBeTested but is unable to find one of its dependencies and complains about it via that error. Basically you need to have a #Bean of that type in your test context. An option will be to create a TestConfig class which provides a Mock/Spy of that dependency via #Bean annotation. In your test you will have to load inside the context via the #ContextConfiguration annotation this test config you just created.
https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#spring-testing-annotation-contextconfiguration
Just mock that dependency. Assuming that AnnoyingServiceWhichIsADependency is an interface:
#RunWith(SpringRunner.class)
#WebMvcTest(ClassToBeTested.class)
public class ClassToBeTestedTests {
#Autowired
private MockMvc mockMvc;
#MockBean
private AnnoyingServiceWhichIsADependency annoyingDependency;
#Test
public void simpleTestMethodToGetClassWorking(){
Assert.assertTrue(true);
}
}
Use Mockito when and thenReturn methods to instruct the mock.
In my Spring boot 2.1 project I have different #Configurations for different test (ConfigurationA and ConfigurationB), that reside in different packages. Both configurations define the same set of beans but in a different manner (mocked vs. the real thing)
As I am aware of the Bean overriding mechanism introduced in Spring Boot 2.1, I have set the property: spring.main.allow-bean-definition-overriding=true.
However I do have a test with the following the setup of the following configuration and test class. First there is a #Configuration in the productive part (I'm using Maven):
package com.stackoverflow;
#Configuration
public class ProdConfiguration{
...
}
Then in the test branch there is a general Test #Configuration on the same package level:
package com.stackoverflow
#Configuration
public class TestConfiguration {
#Bean
public GameMap gameMap() {
return Mockito.mock(GameMap.class);
}
}
And in a subpackage I have another #Configuration:
package com.stackoverflow.impl;
#Configuration
public class RealMapTestConfiguration {
#Bean
public GameMap gameMap() {
return new GameMap("testMap.json");
}
}
And then of course there is the test that is troubling me:
package com.stackoverflow.impl;
#ExtendWith(SpringExtension.class)
#SpringBootTest
#ContextConfiguration(classes={RealMapTestConfiguration.class, ProdConfiguration.class})
#ActiveProfiles("bug") // spring.main.allow-bean-definition-overriding=true
public class MapImageServiceIT {
#Autowired
private GameMap map;
}
It turns out that the injected GameMap into my test is a mock instance from TestConfiguration instead of the real thing from RealMapTestConfiguration. Aparrently in my test I have the configuration from ProdConfiguration and TestConfiguration, when I wanted ProdConfiguration and RealMapTestConfiguration. As the beans defined in the ProdConfiguration and *TestConfiguration are different the combination works, but TestConfiguration and RealMapTestConfiguration define the same been. It seems like the TestConfiguration is picked up by component scanning as it is in the same package as ProdConfiguration.
I was under the impression that when overriding beans the bean definition that is closer to the test class would be preferred. However this seems not to be the case.
So here are my questions:
When overriding beans, what is the order? Which bean overrides which one?
How to go about to get the correct instance in my test (using a different bean name is not an option, as in reality the injected bean is not directly used in the test but in a service the test uses and there is no qualifier on it.)
I've not used the spring.main.allow-bean-definition-overriding=true property, but specifying specific config in a test class has worked fine for me as a way of switching between objects in different tests.
You say...
It turns out that the injected GameMap into my test is a mock instance from TestConfiguration instead of the real thing from RealMapTestConfiguration.
But RealMapTestConfiguration does return a mock
package com.stackoverflow.impl;
#Configuration
public class RealMapTestConfiguration {
#Bean
public GameMap gameMap() {
return Mockito.mock(GameMap.class);
}
}
I think the problem here is that including ContextConfiguration nullifies (part of) the effect of #SpringBootTest. #SpringBootTest has the effect of looking for #SpringBootConfiguration in your application (starting from the same package, I believe). However, if ContextConfiguration is applied, then configurations are loaded from there.
Another way of saying that: because you have ContextConfiguration in your test, scanning for #Configuration classes is disabled, and TestConfiguration is not loaded.
I don't think I have a full picture of your configuration setup so can't really recommend a best practice here, but a quick way to fix this is to add TestConfiguration to your ContextConfiguration in your test. Make sure you add it last, so that it overrides the bean definitions in the other two configurations.
The other thing that might work is removing #ContextConfiguration entirely and letting the SpringBootApplication scanning do its thing - that's where what you said about the bean definition that is closest may apply.
In that case just don't use #Configuration on configuration class and import it to the test manually using #Import, example:
#SpringBootTest
#Import(MyTest.MyTestConfig.class)
public class MyTest {
#Autowired
private String string;
#Test
public void myTest() {
System.out.println(string);
}
static class MyTestConfig {
#Bean
public String string() {
return "String";
}
}
}
I'm new to Spring and testing a Spring Data project with Postgresql/JPA and MongoDB components. My Test class has the following annotations:
#SpringBootApplication
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {SpringMongoConfig.class, PgRepository.class, MongodbRepository.class})
public class PerfTest {
#Autowired
private PgRepository pgRepo;
#Autowired
private MongodbRepository mongoRep;
For some reason the spring-context module is trying to load WebMvcConfigurer while resolving bean classes, giving me a NoClassDefFoundError.
Is the Spring Boot Autoconfigure trying to initiate a full web controller suite? If so, why? I'm really just interested in Spring Data. Should I avoid Spring Boot entirely?
You shouldn't annotate a test class as a #SpringBootApplication. This annotation is used to define a class in your main code base which contains a main() method to spin up your Spring boot container with auto-configuration.
If it's Spring data you are interested in testing then your tests will need to point to a #Configuration class which is annotated with #EnableJpaRepositories this will allow your repository interfaces to be autowired.
I don't know what is in your SpringMongoConfig class but if this contains that annotation then all you need to do is:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {SpringMongoConfig.class})
public class PerfTest {
If not then you need to set up this configuration and include it in the #ContextConfiguration of the test.
Another option which is a bit more heavyweight but will guarantee everything is available for your test (provided your Spring boot application is set up correctly) is to mark your test with #SpringBootTest this will automatically load the whole Spring boot context for the application. If it has trouble finding your main class you can point it in the right direction by providing the class in the annotation:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MyMainClass.class)
public class PerfTest {
You need to add #Component to your repository interface. Spring loads the component class first and then #AutoWiring & Initialization of variables will happen.