i don't get it: The Application code is executed during my integration tests.
Here is my Application class:
#SpringBootApplication
public class Application implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Autowired
SurveyService surveyService;
#Override
public void run(String... args) throws Exception {
System.out.println("Hello world");
// some useage of the autowired service (do all the stuff)
}
}
The SurveyService consume just some REST API.
My test looks something like that:
#ExtendWith(SpringExtension.class)
#RestClientTest({SurveyRestService.class})
#ComponentScan("com.example.app")
#TestPropertySource(properties = "uri=http://testMe.com/")
class SurveyRestServiceTest {
#Autowired
SurveyService classUnderTest;
#Autowired
MockRestServiceServer mockServer;
private void setupMockServerAndRespond(String response) {
mockServer.expect(requestTo("http://testMe.com/surveys")).andRespond(withSuccess(response, APPLICATION_JSON));
}
#Test
void shouldDeserialzeAllFields() {
setupMockServerAndRespond(VALID_JSON_ONE_ENTRY);
List<Survey> surveys = classUnderTest.listSurveys();
assertThat(surveys).hasSize(1);
// ...
}
}
If i execute the test i always see Hello world (see Application class). Why is the Application code executed? It also executed, when I remote the SpringApplication.run call.
In production mode my App should start, execute some REST calls and than terminate. So I put all the executions in my Application class. But this executions should not be called in test case. How can I achieve this?
Thanks :)
add to SurveyRestServiceTest :
#SpringBootTest(classes = Application.class)
Related
I have an issue with rollbacking in #Transactional. When rollback is called, rollback itself is working, but another method after is executed.
#Transactional(rollbackFor = { Exception.class })
#Service
public class Service {
public void method1() {
stuffToGetListOfObjects();
deleteAllAndSaveAll(listOfObejcts);
Util.staticMethod();
}
private deleteAllAndSaveAll(List list) {
repository.deleteAll();
repository.saveAll(list);
}
}
#SpringBootApplication
public class Application implements ApplicationRunner {
private final Service service;
#Autowired
public Application(Service service) {
this.service = service;
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public void run(ApplicationArguments args) throws Exception {
service.method1();
}
}
When something goes wrong during the insertion in repository.saveAll(list), no data is actually deleted which is fine and expected. The issue is program is going on the Util.staticMethod() method is executed "after" rollback.
I know that static methods cannot be #Transactional and private methods are ignored, but that doesn't seem the issue here. According to the log, everything in method1() is executed first and after that, the inserting is happening. I guess I need to pick out the Util.staticMethod() calling from transaction somehow.
That's why you should separate the repository actions from the service functions. Repository actions are rollbacked. If you separate them the repository level will throw an error and rollback itself. And before the static method, you can check whether the transaction is finished or not by #transactionaleventlistener.
Say i have a repository:
public interface UserRepository extends JpaRepository<User, Long>{
List<User> findAll();
}
If i want to test out what the result will be (using System.out.println)
I know only one way:
Make a Controller class like so:
#RestController
#RequestMapping("/hey")
class Controller{
#Autowired
UserRepository repository;
#GetMapping()
public void hey(){
System.out.println(repository.findAll());
}
}
And then i have to start up the SpringBootApplication:
#SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
surf to localhost:8080/hey
And only then i see on the console what the result is.
This is so cumbersome and time-consuming, especially when the spring boot app is heavy and takes a long time to load.
What i would have loved to do is to comment out the springbootapplication and just make a temporary class that i run from the main class and print the results that way. That is so much faster.
#SpringBootApplication
public class App {
public static void main(String[] args) {
//SpringApplication.run(App.class, args);
new Temp().go();
}
}
Class Temp{
#Autowired
UserRepository repository;
public void go(){
System.out.println(repository.findAll());
}
}
This isn't possible as its an interface and it asks you to implement every single method it has. How can i just print the results on the console of a repository without having to use controller ?
As you mentionned if you comment SpringApplication.run Spring DI won't work because the Spring context is not yet configured with required beans.
What you can do to achieve your goal, first you need to keep SpringApplication.run and you can inject the repository as follows in your main class:
#SpringBootApplication
public class App{
public static UserRepository repository;
#Autowired
public App(UserRepository repository) {
App.repository= repository;
}
public static void main(String[] args) {
SpringApplication.run(App.class, args);
System.out.println(repository.findAll());
}
}
So I am trying to autowire my http object in my test class and I have tried to integrate with #SpringBootTest however my http object still remains null.
My test class looks like this.
//#RunWith(SpringRunner.class)
#SpringBootTest(classes=Http.class)
public class GetItemTests {
private static final Logger LOGGER = LoggerFactory.getLogger(GetItemTests.class);
#Autowired
private Http httpClass;
}
My SpringBootMain class looks like this
#SpringBootConfiguration
#SpringBootApplication
public class SpringBootMain implements CommandLineRunner {
#Bean
ResourceConfig resourceConfig() {
return new ResourceConfig().registerClasses(Version1Api.class,TokenUtilityClass.class, Paypal.class);
}
#Override
public void run(String... args) throws Exception {
//test.authenticationToken();
}
public static void main(String[] args) {
SpringApplication.run(SpringBootMain.class);
}
}
I have tried running with the SpringRunner as well as this but I receive errors about failing to load the application context.
Unless your Http class is annotated with #Component (meaning it is a bean maintained by the Spring IoC Container) you will not be able to #Autowire it in the way you wrote.
Please also post the exception you are getting and the implementation of your Http class so we can potentially provide further help.
I am trying to have my test unit up and running, and I have encountered a weird issue. My application uses an ApplicationListener class annotated as a #Component to perform an operation during startup.
During tests I have mocked the service that contains the logic, but I found that even though Mockito's when instructions work well in controller scope, the bean is not initialized for this ApplicationListener class: instead of returning what I define in the test unit, it returns either false or null - depending on the data type returned by each method in the service.
Since I have not found any way to initialize the mocked service from the test unit for the ApplicationListener class, I have decided to exclude it. To do so I have tried different approaches, being the one most often used that of creating a test application context and change its configuration. Unfortunately, nothing I have seen is working - so I am here asking for help. If possible, I would prefer not touching the ApplicationListener class and do all related coding in the test code.
I am interested in any of the two possible solutions, if they can be done:
1.- Get the mocked behaviour during the ApplicationListener execution, but I have read somewhere that this cannot be done
2.- Exclude the #Component from the test unit somehow.
TestUnit.Java:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = TestApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
#AutoConfigureMockMvc
public class TestConfigurationService {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#MockBean
private MockService mockService;
private void initMockBean () throws Exception {
when(mockService.isDoingSomething()).thenReturn(true);
}
#Before
public void setup() throws Exception {
// Spring mock context application setup
this.mockMvc = webAppContextSetup(webApplicationContext).build();
// Initialize ConsulService mock bean
initMockBean ();
}
}
TestApplication.java
#SpringBootApplication
#EnableAutoConfiguration
#ComponentScan(basePackages="my.base.package", excludeFilters = #Filter(type = FilterType.ASSIGNABLE_TYPE, classes = StartupConfiguration.class))
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
Besides what is shown in the code, I have also tried this annotation in file TestApplication.java:
#SpringBootApplication(exclude={StartupConfiguration.class})
StartupConfiguration.java
#Component
public class StartupConfiguration implements ApplicationListener<ContextRefreshedEvent> {
#Autowired
private ConfigurationService configurationService;
#Override
public void onApplicationEvent(final ContextRefreshedEvent event) {
try {
configurationService.updateConfiguration();
} catch (Exception e) {
throw new RuntimeException ("Error", e);
}
}
}
ConfigurationService.java
public interface ConfigurationService {
public void updateConfiguration () throws Exception;
}
ConfigurationServiceImpl.java
#Service
#Transactional
public class ConfigurationServiceImpl implements ConfigurationService {
#Autowired
private MService mockService;
#Override
public void updateConfiguration() throws Exception {
if (mockService.isDoingSomething()==false)
throw new Exception ("Something went wrong");
}
}
Versions:
Spring Boot 1.5.4.RELEASE,
Java 1.8
You can create mock bean of the same type and mark it with #Primary annotation to replace real bean. You can achieve this by having test such configuration:
#Configuration
#Import(TestApplication.class)
public class TestConfiguration {
#Bean
#Primary
public ConfigurationService configurationService() {
return Mockito.mock(ConfigurationService.class);
}
}
then get this mock in test:
...
public class TestConfigurationService {
...
#Autowired
ConfigurationService configurationService;
#Before
public void setUp() {
when(mockService.isDoingSomething()).thenReturn(true);
}
}
Thanks, araxn1d. Your answer gave me the clue to solve this issue.
I mocked the StartupConfiguration class in TestUnit.java:
#MockBean
private StartupConfiguration startupConfiguration;
Though in this case I was lucky: application listeners don't have returning methods, so they don't need when test configuration. If I had required that some method there returned for example true or a value, this method would not apply.
But at least for application listeners, this is enough.
Does anyone knows how to add a test Resource (i.e. one that is only for testing purposes and not added in run() method of the app)?
Here is an example:
public class MyTest {
#ClassRule
public static final DropwizardAppRule<TestConfiguration> RULE =
new DropwizardAppRule<TestConfiguration>(MyApp.class, "my-app-config.yaml");
#BeforeClass
public static void setUpBeforeClass() throws Exception
{
MyTest.RULE.getEnvironment().jersey().register(new JustForTestingResource());
}
#Test
public final void testTestResource()
{
Client client = new Client();
ClientResponse response = client.resource(
String.format("http://localhost:%d/rest/v1/test", RULE.getLocalPort()))
.get(ClientResponse.class);
assertThat(response.getStatus(), is(200));
}
}
and
public class JustForTestingRessource {
#GET
#Path("test")
#Produces(MediaType.APPLICATION_JSON)
public Response getInTestResource()
{
return Response.status(Status.OK).type(MediaType.TEXT_PLAIN).entity("get #Path(\"test\") is ok").build();
}
}
My problem is that the added resource is not added and I get resource not found 404 error response. It seems that I am registering the new resource after resource publishing and there is no refresh inside Dropwizard after start.
I dont want to extend my Application class and I dont want to insert test code into my real application code. Does anyone knows how to register the test resource without registering it in run() method of the Application?
This works, but a new class is needed:
public class TestService extends MyService{
#Override
public void run(
TestConfigurationconfiguration,
Environment environment) throws ClassNotFoundException
{
environment.jersey().register(new JustForTestingRessource());
super.run(configuration,environment);
}
}
Call in JUnit as already known:
#ClassRule
public static DropwizardAppRule<TestConfiguration> RULE =
new DropwizardAppRule<TestConfiguration>(TestService.class, "my-app-config.yaml");
Edit: Removing previous answer because it didn't solve your problem the way you wanted to do it.
I dug into the environment startup code and realized the reason why registering a controller didn't make it available is because jetty had already been started. If you stop jetty, register your controller and start jetty back up again, your resource will be available and you can use it in your test.
#BeforeClass
public static void setUpBeforeClass() throws Exception
{
MyTest.RULE.environment.applicationContext.stop()
MyTest.RULE.environment.jersey().register(new JustForTestingResource())
MyTest.RULE.environment.applicationContext.start()
}
You can test the Resource itself in a Jersey Container without starting a full dw-instance.
Check the "Testing Resources" section.
import static org.fest.assertions.api.Assertions.assertThat;
import static org.mockito.Mockito.*;
public class PersonResourceTest {
private static final PeopleStore dao = mock(PeopleStore.class);
#ClassRule
public static final ResourceTestRule resources = ResourceTestRule.builder()
.addResource(new PersonResource(dao))
.build();
private final Person person = new Person("blah", "blah#example.com");
#Before
public void setup() {
when(dao.fetchPerson(eq("blah"))).thenReturn(person);
// we have to reset the mock after each test because of the
// #ClassRule, or use a #Rule as mentioned below.
reset(dao);
}
#Test
public void testGetPerson() {
assertThat(resources.client().resource("/person/blah").get(Person.class))
.isEqualTo(person);
verify(dao).fetchPerson("blah");
}
}
I had the similar issue with the #ClassRule, maybe it can help to somebody..
In my test (Groovy) the invocation of RULE.getApplication() or getEnvironment() from #BeforeClass method returned null:
def setupSpec() {
RULE.application.run()
}
shown
java.lang.NullPointerException: Cannot invoke method run() on null object
I.e. RULE.testSupport had both null application and environment.
I found out that the call to
RULE.testSupport.before()
just before run() solves the error:
def setupSpec() {
RULE.testSupport.before()
RULE.application.run()
}
And then #AfterClass method:
def cleanupSpec() {
RULE.testSupport.after()
}
Or just use #Rule instead of #ClassRule and call
def setup() {
RULE.application.run()
}
inside of #Before method instead of #BeforeClass.
Though It seems strange, maybe there is some other better solution exists..
public class TestMain extends Main{
public static void main(String ... args) throws Exception {
new TestMain().run(args);
}
#Override
public void initialize(Bootstrap<AppConfiguration> bootstrap) {
super.initialize(bootstrap);
bootstrap.addBundle(
new MigrationsBundle<AppConfiguration>() {
#Override
public DataSourceFactory getDataSourceFactory(
AppConfiguration configuration) {
return configuration.getDataSourceFactory();
}
});
}
}