I believe this is a very particular case, but I am building some cucumber tests for some third-party applications we use.
Since I am not really testing my own application, I created a maven project and configured cucumber to run in the main folder (not the test folder).
This is my entrypoint class:
#SpringBootApplication
public class ExecutableMain implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(ExecutableMain.class, args);
}
#Override
public void run(String... args) {
// args logic...
JUnitCore.runClasses(MyCucumberTest.class);
}
}
And my test class:
#RunWith(Cucumber.class)
#CucumberOptions(
plugin = {"pretty", "html:target/cucumber", "json:target/cucumber/cucumber.json"},
glue = {"cucumber.app", "cucumber.steps"}
)
public class MyCucumberTest {
#AfterClass
public static void tearDown(){
// quit the browser
}
}
This currently works fine, but I want to add spring features to my tests.
Specifically, I want to autowire something in my cucumber steps.
Stepdefs:
public class MyStepdefs {
#Autowired
private ConfigProperties properties;
#Given("^Something")
public void example() {
//...
}
I searched around and found people saying I should add the ContextConfiguration annotation in the steps. I did it like so:
#ContextConfiguration(classes = ExecutableMain.class, loader = SpringBootContextLoader.class)
public class MyStepdefs {
But this resulted in a loop during start up.
Can I achieve what I need?
Ok, so I got it to work following https://stackoverflow.com/a/37586547/1031162
Basically I changed:
#ContextConfiguration(classes = ExecutableMain.class, loader = SpringBootContextLoader.class)
To:
#ContextConfiguration(classes = ExecutableMain.class, initializers = ConfigFileApplicationContextInitializer.class)
I am not 100% sure how/why it worked, but it did.
Related
I have a customized spring-boot-starter which will call some REST APIs when it gets a spring application event of ApplicationReadyEvent, so the configuration class is something like:
#Configuration
public class MySpringBootStarter {
#EventListener(ApplicationReadyEvent.class)
public void init() {
// Call REST APIs here
}
}
Then, I want to test the starter using MockServer which requires creating some expectations before the test runs. The test class may look like as follows:
#ExtendWith(MockServerExtension.class)
#SpringBootTest
#ContextConfiguration
#MockServerSettings(ports = {28787, 28888})
public class MySpringBootStarterTest {
private MockServerClient client;
#BeforeEach
public void beforeEachLifecycleMethod(MockServerClient client) {
this.client = client;
//creating expectations here
}
#Test
void shouldBeTrue() {
assertThat(true).isTrue();
}
#SpringBootApplication
static class MyTest {
public void main(String[] args) {
SpringApplication.run(Test.class, args);
}
}
}
But in fact, the expectations are always created after the ApplicationReadyEvent, viz., the init method of MySpringBootStarter class is called before the the beforeEachLifecycleMethod method in MySpringBootStarterTest class.
How can I make the test work, please?
You can use static block initializer to run required code before SpringContext boots up.
I have a basic spring data application and I have written a unit test. What appears to happen is that when I run the Spring test my application run method gets called as well. I would like to know why this is and how to stop it please.
I have tried using active profiles but that doesnt fix the problem
#SpringBootApplication
#EntityScan({ "com.demo" })
public class Application implements ApplicationRunner {
#Autowired
private IncrementalLoadRepository repo;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public void run(ApplicationArguments args) throws Exception {
IncrementalLoad incrementalLoad = new IncrementalLoad("fred", Instant.now(), Instant.now(), Instant.now());
repo.save(incrementalLoad);
}
and the unit test........
#RunWith(SpringRunner.class)
#SpringBootTest(classes = { Application.class })
#ActiveProfiles("test")
public class IncrementalLoadServiceTest {
#Autowired
private IncrementalLoadService incrementalLoadService;
#Test
public void checkInitialRecords_incrementalLoad() {
List<IncrementalLoad> incrementalLoads = incrementalLoadService.list();
assertEquals(3, incrementalLoads.size());
}
So I think I found the solution. I created another #SpringBootApplication class in my test folders. Initially that failed but I believe thats because the entity scan annotation pointed to packages where my "production" #SpringBootApplication was. I moved that class up a level and it all seems to work ok now.
I have below test for my spring boot main method.
The test tries to start the application 2 times which is expected.
First time when it starts the application it uses the Mock object hewever 2nd time it starts the application it calls the actual bean.
I have ReferenceDataService having #PostConstract method call which makes rest call to some other application which I don't want in my tests.
Another thing is that MqConfiguration which tries to connect to IBM queues that also I would like to avoid in my test.
Please note even though I have added #ComponentScan(excludeFilters... in my test class it does not exclude it.
How do I write test for my main method in this case?
#ActiveProfiles(profiles = {"test"})
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MainApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT, properties = {
"camel.springboot.java-routes-include-pattern=**/NONE*"})
#EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, SecurityAutoConfiguration.class})
#DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
#ComponentScan(excludeFilters = {#ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {MqConfiguration.class, ReferenceDataCache.class})})
public class MainApplicationTest {
#MockBean
private MqService mqService;
#MockBean
private ReferenceDataService referenceDataService;
#SpyBean
private ReferenceDataCache cacheSpy;
#Test
public void test() {
Mockito.when(referenceDataService.getCurrencies()).thenReturn(new HashMap<>());
Mockito.when(referenceDataService.getFrequencies()).thenReturn(null);
Mockito.when(referenceDataService.getDayCountTypes()).thenReturn(null);
Mockito.when(referenceDataService.getBusinessDayConverntions()).thenReturn(null);
Mockito.when(referenceDataService.getRateDefinations()).thenReturn(null);
Mockito.when(referenceDataService.getBusinessCalendar()).thenReturn(null);
Mockito.when(referenceDataService.getFinancingTypes()).thenReturn(null);
Mockito.when(referenceDataService.getStaffs()).thenReturn(null);
MainApplication.main(new String[]{});
}
}
MainApplication.java (The class to be tested)
#SpringBootApplication
#EnableJms
#EnableCaching
#AutoConfigureBefore(JmsAutoConfiguration.class)
public class MainApplication {
private static final Logger logger = LoggerFactory.getLogger(MainApplication.class);
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
One could split it into two separate testing parts as we should strive to test a single functionality per test (Single Responsibility Principle). You could model your testing like below:
#Test
public void applicationContextLoadedTest(){
}
#Test
public void applicationStartTest() {
//you can add your mocks as per your required dependencies and requirements
MainApplication.main(new String[] {});
}
Alternatively, if you are allowed to use PowerMockito, then the following link gives you a working example for verifying static invocations.PowerMockito - SpringBoot test
I have the standard Application class which runs a Spring batch ETL:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
with my Junit test doing something like:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class MyServiceTest {
#Autowired
MyService myService;
#Test
public void testInsertions() {
//do stuff with assertions
}
}
My problem is that when I execute the junit test, the application also kicks off the ETL then executes the test. How to prevent the application from running?
I think there are a lot of alternatives and it really depends on what you are trying to achieve.
One of the options would be to run your tests with a specific profile, like testing, and configure your ETLs (I'm assuming they are just jobs) to be configured based on some property or specific profile.
For example:
Testing class
#ActiveProfiles("testing")
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class MyServiceTest {
...
}
Job/ETL classes
#Component
#Profile("!testing")
public class JobEtlService {
}
Hope it helps.
I am experiencing rather strange thing when using Spring Boot. Lets get with it.
I have an app which, when ran from spring-boot:run, loads perfectly fine and I can use my server. However, if I try to run tests (either via launching test from IntelliJ or via surefire plugin) context fails to load.
Issue lies within this class (only relevant part shown):
#RestController
#RequestMapping(
value = "/sa/revisions/"
)
#SuppressWarnings("unchecked")
class RevisionController {
#Autowired
// cant autowire this field
private RepositoryEntityLinks repositoryEntityLinks = null;
/* omitted */
}
And here is my main class:
#EnableAsync
#EnableCaching
#EnableAutoConfiguration
#EnableConfigurationProperties
#Import({
SecurityConfiguration.class,
DataConfiguration.class,
RestConfiguration.class
})
public class SpringAtomApplication {
#Autowired
private DataLoaderManager dataLoaderManager = null;
public static void main(String[] args) {
SpringApplication.run(SpringAtomApplication.class, args);
}
#Bean
public CacheManager cacheManager() {
final GuavaCacheManager manager = new GuavaCacheManager();
manager.setAllowNullValues(false);
return manager;
}
#PostConstruct
private void doPostConstruct() {
this.dataLoaderManager.doLoad();
}
}
As I said, application loads without an issue when ran normally, however when it comes to this simple test, everything falls apart:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = SpringAtomApplication.class)
public class SpringAtomApplicationTests {
#Test
public void contextLoads() {
}
}
Would appreciate any suggestion, because I'd love to start with testing it.
You should set SpringApplicationContextLoader in your test class:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(
classes = SpringAtomApplication.class,
loader = SpringApplicationContextLoader.class)
public class SpringAtomApplicationTests {
#Test
public void contextLoads() {
}
}
With that you can test non-web features (like a repository or a service) or start an fully-configured embedded servlet container and run your tests using MockMvc.
Reference: http://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/SpringApplicationContextLoader.html