I have a qustion:
what would be the right configuraiton to load only the Enpoint class that I want to test in that integration test and not the full Application context (not All the Enpoint Classes)?
Right now I have:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {WebServiceConfigTest.class}, properties = {"application.address=http://hostname:port/context"})
public class MyEndpointTest {
#Autowired
private ApplicationContext applicationContext;
private MockWebServiceClient mockClient;
#Before
public void init() {
mockClient = MockWebServiceClient.createClient(applicationContext);
MockitoAnnotations.initMocks(this);
}
#Test
public void test1(){}
....
}
in WebServiceConfigTest is:
#ComponentScan("mypackages.soap")
#ContextConfiguration(classes = SoapApplication.class)
#MockBean(classes = {MyService.class})
public class WebServiceConfigTest {
}
SoapApplication is:
#ComponentScan({"mypackages"})
#SpringBootApplication
public class SoapApplication extends SpringBootServletInitializer implements WebApplicationInitializer {
public static void main(String[] args) {
SpringApplication.run(SoapApplication.class, args);
}
}
The reason is that in Soap module I have a dependency of Service module which has other dependencies as well and so on.
If i load the whole ApplicaitonContext then:
either I need to mock the full list of services that I use in Soap module
or to mock the underneath dependencies of Service module, such as DataSource, Queues etc.
If I do the second will make Soap module aware of things it should not be.
If I do the first I am forced to mock and maintain in the config test file the full list of used Services which can be long.
Any advices here?
what would be the right configuration to load only the Endpoint class that I want to test in that integration test and not the full Application context
you can ask spring to only instantiated particular Controller class but not loading complete application context by using #WebMvcTest(MyEndpoint.class)
#RunWith(SpringRunner.class)
#WebMvcTest(MyEndpoint.class)
public class MyEndpointTest {
#MockBean //mock all service beans that are injected into controller
private Service service;
#Autowired
private MockMvc mockMvc;
#Test
public void test1(){}
....
}
}
I would also recommend in doing end to end integration testing using #SpringBootTest if you are using embedded database (for example H2), or embedded queues for testing
After long searches and trials i found the solution.
As you can ask Spring on REST to loaad only 1 Controller into the context with
#WebMvcTest(MyController.class)
the same thign you can do for Soap with
#SpringBootTest(classes = {MyEndpoint.class})
It will load only the Endpoint you want and you can mock the the Services you use inside that or you can go all the way till the Repository o whatever your Application Bussiness logic is doing there.
Related
Java 8, JUnit 4 and Spring Boot 2.3 here. I have a situation where I have a #Component-annotated Spring Boot class that gets #Autowired with all its dependencies (the beans are defined in a #Configuration-annotated config class):
#Configuration
public class SomeConfig {
#Bean
public List<Fizz> fizzes() {
Fizz fizz = new Fizz(/*complex logic here*/);
return new Collections.singletonList(fizz);
}
#Bean
public Buzz buzz() {
return new Buzz(/*complex logic here*/);
}
}
#Component
public class ThingDoerinator {
#Autowired
private Lizz<Fizz> fizzes;
#Autowired
private Buzz buzz;
public String doStuff() {
// uses fizz and buzz extensively inside here...
}
}
I can easily write a unit test to inject all of these dependencies as mocks:
public class ThingDoerinatorTest extends AbstractBaseTest {
#Mock
private List<Fizz> fizzes;
#Mock
private Buzz buzz;
#InjectMocks
private ThingDoerinator thingDoerinator;
#Test
public void should_do_all_the_stuff() {
// given
// TODO: specify fizzes/buzz mock behavior here
// when
String theThing = thingDoerinator.doStuff();
// then
// TODO: make some assertions, verify mock behavior, etc.
}
}
And 80% of the time that works for me. However I am now trying to write some unit tests that are more like integration tests, where instead of injected mocks, I want the beans to be instantiated like normal and get wired into the ThingDoerinator class like they would be in production:
public class ThingDoerinatorTest extends AbstractBaseTest {
#Mock
private List<Fizz> fizzes;
#Mock
private Buzz buzz;
#InjectMocks
private ThingDoerinator thingDoerinator;
#Test
public void should_do_all_the_stuff() {
// given
// how do I grab the same Fizz and Buzz beans
// that are defined in SomeConfig?
// when -- now instead of mocks, fizzes and buzz are actually being called
String theThing = thingDoerinator.doStuff();
// then
// TODO: make some assertions, verify mock behavior, etc.
}
}
How can I accomplish this?
You can use SpringBootTest.
#SpringBootTest(classes = {Fizz.class, Buzz.class, ThingDoerinator.class})
#RunWith(SpringRunner.class)
public class ThingDoerinatorTest {
#Autowired
private Fizz fizz;
#Autowired
private Buzz buzz;
#Autowired
private ThingDoerinator thingDoerinator;
}
You don't need to mock anything, just inject your class in your test and your configuration will provide the beans to your ThingDoerinator class and your test case will work as if you are calling the ThingDoerinator. doStuff() method from some controller or other service.
TL;DR
I think, you are confused with tests running whole spring context and unit test which just test the function/logic we wrote without running whole context. Unit tests are written to test the piece of function, if that function would behave as expected with given set of inputs.
Ideal unit test is that which doesn't require a mock, we just pass the input and it produces the output (pure function) but often in real application this is not the case, we may find ourselves in situation when we interact with some other object in our application. As we are not about to test that object interaction but concerned about our function, we mock that object behaviours.
It seems, you have no issue with unit test, so you could mock your List of bean in your test class ThingDoerinator and inject them in your test case and your test case worked fine.
Now, you want to do the same thing with #SpringBootTest, so I am taking a hypothetical example to demonstrate that you don't need to mock object now, as spring context will have them, when #springBootTest will load whole context to run your single test case.
Let's say I have a service FizzService and it has one method getFizzes()
#Service
public class FizzService {
private final List<Fizz> fizzes;
public FizzService(List<Fizz> fizzes) {
this.fizzes = fizzes;
}
public List<Fizz> getFizzes() {
return Collections.unmodifiableList(fizzes);
}
}
Using constructor injection here1
Now, my List<Fizzes would be created through a configuration (similar to your case) and been told to spring context to inject them as required.
#Profile("dev")
#Configuration
public class FizzConfig {
#Bean
public List<Fizz> allFizzes() {
return asList(Fizz.of("Batman"), Fizz.of("Aquaman"), Fizz.of("Wonder Woman"));
}
}
Ignore the #Profile annotation for now2
Now I would have spring boot test to check that if this service will give same list of fizzes which I provided to the context, when it will run in production
#ActiveProfiles("test")
#SpringBootTest
class FizzServiceTest {
#Autowired
private FizzService service;
#Test
void shouldGiveAllTheFizzesInContext() {
List<Fizz> fizzes = service.getFizzes();
assertThat(fizzes).isNotNull().isNotEmpty().hasSize(3);
assertThat(fizzes).extracting("name")
.contains("Wonder Woman", "Aquaman");
}
}
Now, when I ran the test, I saw it works, so how did it work, let's understand this. So when I run the test with #SpringBootTest annotation, it will run similar like it runs in production with embedded server.
So, my config class which I created earlier, will be scanned and then beans created there would be loaded into the context, also my service class annotated with #Service annotation, so that would be loaded as well and spring will identify that it needs List<Fizz> and then it will look into it's context that if it has that bean so it will find and inject it, because we are supplying it from our config class.
In my test, I am auto wiring the service class, so spring will inject the FizzService object it has and it would have my List<Fizz> as well (explained in previous para).
So, you don't have to do anything, if you have your config defined and those are being loaded and working in production, those should work in your spring boot test the same way unless you have some custom setup than default spring boot application.
Now, let's look at a case, when you may want to have different List<Fizz> in your test, in that case you will have to exclude the production config and include some test config.
In my example to do that I would create another FizzConfig in my test folder and annotate it with #TestConfiguration
#TestConfiguration
public class FizzConfig {
#Bean
public List<Fizz> allFizzes() {
return asList(Fizz.of("Flash"), Fizz.of("Mera"), Fizz.of("Superman"));
}
}
And I would change my test a little
#ActiveProfiles("test")
#SpringBootTest
#Import(FizzConfig.class)
class FizzServiceTest {
#Autowired
private FizzService service;
#Test
void shouldGiveAllTheFizzesInContext() {
List<Fizz> fizzes = service.getFizzes();
assertThat(fizzes).extracting("name")
.contains("Flash", "Superman");
}
}
Using different profile for tests, hence #ActiveProfile annotation, otherwise the default profile set is dev which will load the production FizzConfig class as it would scan all the packages ( 2Remember the #Profile annotation above, this will make sure that earlier config only runs in production/dev env ). Here is my application.yml to make it work.
spring:
profiles:
active: dev
---
spring:
profiles: test
Also, notice that I am importing my FizzConfiguration with #Import annotation here, you can also do same thing with #SpringBootTest(classes = FizzConfig.class).
So, you can see test case has different values than in production code.
Edit
As commented out, since you don't want test to connect to database in this test, you would have to disable auto configuration for spring data JPA, which spring boot by default does that.
3You can create a another configuration class like
#Configuration
#EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
public TestDataSourceConfiguration {}
And then include that with your #SpringBootTest, this will disable default database setup and then you would be left with last piece of puzzle which is any repository interaction you might be having in your class.
Because now, you don't have auto configuration in your test case, those repository are just interfaces, which you can easily mock inside your test class
#ActiveProfiles("test")
#SpringBootTest
#Import(FizzConfig.class, TestDataSourceConfiguration.class)
class FizzServiceTest {
#MockBean
SomeRepository repository;
#Autowired
private FizzService service;
#Test
void shouldGiveAllTheFizzesInContext() {
// Use Mockito to mock behaviour of repository
Mockito.when(repository.findById(any(), any()).thenReturn(Optional.of(new SomeObject));
List<Fizz> fizzes = service.getFizzes();
assertThat(fizzes).extracting("name")
.contains("Flash", "Superman");
}
}
1Constructor injection is advisable instead of `#Autowired` in production code, if your dependencies are really required for that class to work, so please avoid it if possible.
3Please note that you create such configuration only in test package or mark with some profile, do not create it in your production packages, otherwise it will disable database for your running code.
I have a library-module written using Spring Boot 1.5.21.
The library has a #Service with some methods annotated with #PreAutorize allowing only users with ADMIN authority to perform some actions, for example a delete.
Then I have a Spring Boot Application that uses that library. If I run the app and manually test it, it works. Only ADMINs can delete. I'd like to write test for it.
I was thinking in writing separate test for module and for the main app. In my module I can successfully unit test the operations. But I'm having troubles testing the #PreAuthotize, because the security context does not exist in the module, but in the main app. Is there a way to test that annotation in the module, and not in the main app?
The relevant code of my library:
#Service
public class MyService {
#PreAuthorize("hasAuthority('ADMIN')")
public void delete (long id){
.....
}
}
#RunWith(SpringRunner.class)
public class MyServiceTest {
private MyService service;
#Test
#WithAnonymousUser
public void deleteShouldFailIfAnonymous() {
... Verify exception thrown
}
#Test
#WithMockUser(authorities = 'USER')
public void deleteShouldFailIfNotAdmin() {
... Verify exception thrown
}
#Test
#WithMockUser(authorities = 'ADMIN')
public void deleteShouldPass() {
... Should pass
}
}
I've trying adding #EnableGlobalMethodSecurity(prePostEnabled = true) but with no luck. And as said, the SecurityConfiguration is loaded in the main app, it does not exist in the library-module.
Can I test the #PreAuthorize in the module or should I move it to the main app? I'd like to have it i the module if possible. Or maybe I can create a Context only for testing, but don't know how to do it.
Aspect-oriented features like #PreAuthorize are implemented as advice. Usually, this means that Spring will create a decorator interface that intercepts the method call, performs the advice (in this case, checking the condition and throwing an exception if it fails), and then sends the call on to the service object.
When you use new MyService, you're creating a bare object without the wrappers that implement the advice, so you won't see security, validation, caching, etc., applied. To see the behavior you want, you need to make your test bean #Autowired using the Spring test support.
You can use the MockMvc to test. This will help with module testing incase of integration testing.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
#WebAppConfiguration
public class SecurityTests {
#Autowired
private WebApplicationContext context;
private MockMvc mvc;
#Before
public void setup() {
mvc = MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build();
}
#WithMockUser(roles="ADMIN")
#Test
public void withMockUser() {
mvc.perform(get("......"))
}
}
When I create Spring Boot Application it generates 2 classes:
#SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
And the second on is test:
#RunWith(SpringRunner.class)
#SpringBootTest
public class AppTest {
#Test
public void contextLoads() {
}
}
Like you can notice contextLoads test is empty. How should I provide correct test for contextLoad? It should stay empty maybe? Is it correct? Or I should put something inside?
UPDATE:
Why this test should stay? If I have more tests in application I'll know if spring context is loaded or nope. Isn't it just excessive.
I readed answers like this but it didn't gave me a clear answer.
Actually, the main() method of the Spring Boot Application is not covered by the unit/integration tests as the Spring Boot tests don't invoke the main() method to start the Spring Boot application.
Now I consider that all answers of this post seem overkill.
They want to add a test of the main() method to make a metric tool happy (Sonar).
Loading a Spring context and loading the application takes time.
Don't add it in each developer build just to win about 0.1% of coverage in your application.
I added an answer about that.
Beyond your simple example and the other post that you refer to, in absolute terms it may make sense to create a test for the main() method if you included some logic in. It is the case for example as you pass specific arguments to the application and that you handle them in the main() meethod.
Leave it empty. If an exception occurs while loading the application context the test will fail. This test is just saying "Will my application load without throwing an exception"
Update for Second Question
The reason you want to keep this test is that when something fails you want to find the root cause. If you application context is unable to load, you can assume all other tests that are failing are due to this. If the above test passes, you can assume that the failing tests are unrelated to loading the context.
When you build a Spring boot application using Spring Initializer. It auto creates Application and its Test Class
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
#SpringBootTest
class ApplicationTest {
#Test
void contextLoads() {
}
}
Note the use of #SpringBootTest annotation on test class which tells Spring Boot to look for a main configuration class (one with #SpringBootApplication, for instance) and use that to start a Spring application context. Empty contextLoads() is a test to verify if the application is able to load Spring context successfully or not.
You do not need to provide any test cases for empty method as such. Though you can do something like this to verify your controller or service bean context:-
#SpringBootTest
public class ApplicationContextTest {
#Autowired
private MyController myController;
#Autowired
private MyService myService;
#Test
public void contextLoads() throws Exception {
assertThat(myController).isNotNull();
assertThat(myService).isNotNull();
}
}
I am trying to to rest my rest classes in Spring MVC
If I run the following code (ran fine when the project was small but now fails) it tries to load all the different components in my application.
This includes beans which interact with external systems and need credentials in order to connect
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class TestDummyRest extends BaseRestTestCase{
#Autowired
private MockMvc mockMvc;
#MockBean
private IDummyServices mockDummyServices;
#Test
public void getSendGoodMessage() throws Exception {
given(mockDummyServices.sendGoodMessage(Mockito.anyString())).willReturn(true);
mockMvc.perform(get("/dummy"))
.andExpect(status().isOk())
.andExpect(content().contentType(TEXT_PLAIN_CONTENT_TYPE));
verify(mockDummyServices, times(1)).sendGoodMessage(Mockito.anyString());
}
}
How do I tell my test classes not to load the #Configuration or #Component classes of my application?
Instead of not creating other classes in your application, you could only create the classes you are interested in, see 15.6.1 Server-Side Tests - Setup Options
The second is to simply create a controller instance manually without
loading Spring configuration. Instead basic default configuration,
roughly comparable to that of the MVC JavaConfig or the MVC namespace,
is automatically created and can be customized to a degree:
public class MyWebTests {
private MockMvc mockMvc;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(new AccountController()).build();
}
// ...
}
You need to use #TestComponent and #TestConfiguration for this as explained in Spring doc here
In my Spring Boot project, I have a main class annotated with #SpringBootConfiguration. I also have some unit tests that use #SpringApplicationConfiguration that points to an inner class that defines a Spring context for usage in my unit test (using some mocks).
I now want to write an integration test that starts the full context of my application. However, this does not work as it also picks up the Spring contexts that are defined as inner classes in other unit tests.
What would be the best way to avoid that? I did see the exclude and excludeName properties on #SpringBootConfiguration, but I am unsure how to use them.
UPDATE:
Some code to explain the problem more:
My main class:
package com.company.myapp;
#SpringBootApplication
#EnableJpaRepositories
#EnableTransactionManagement
#EntityScan(basePackageClasses = {MyApplication.class, Jsr310JpaConverters.class})
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
I have a unit test for Spring REST Docs:
package com.company.myapp.controller
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration
#WebAppConfiguration
public class SomeControllerDocumentation {
#Rule
public final RestDocumentation restDocumentation = new RestDocumentation("target/generated-snippets");
// Some test methods here
// Inner class that defines the context for the unit test
public static class TestConfiguration {
#Bean
public SomeController someController() { return new SomeController(); }
#Bean
public SomeService someService() { return new SomeServiceImpl(); }
#Bean
public SomeRepository someRepository() { return mock(SomeRepository.class);
}
So the unit test uses the context defined in the inner class. Now I want a different test that tests if the "normal" application context of the app starts up:
package com.company.myapp;
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(MyApplication.class)
#WebAppConfiguration
public class MyApplicationTest {
#Autowired
private ApplicationContext applicationContext;
#Test
public void whenApplicationStarts_thenContextIsInitialized() {
assertThat(applicationContext).isNotNull();
}
}
This test will now not only wire the stuff it should, but also the beans from the SomeControllerDocumentation.TestConfiguration inner class. This I want to avoid.
You could use profiles: annotate the relevant configuration beans with #Profile("unit-test") and #Profile("integration-test") and inside the tests specify which profile should be active via #ActiveProfiles.
However, it sounds like you could avoid the problem altogether just by reorganising your configuration structure. It's hard to assume anything without seeing any code, though.
Since Spring Boot 1.4, the problem can be avoided by annotation the configuration in the unit tests with #TestConfiguration
I think you talk about #Ignore