How to set 'headless' property in a Spring Boot test? - java

I am testing using Spring Boot with JavaFX (Based on some excellent YouTube videos that explain this).
To make it work with TestFX, I need to create the context like this:
#Override
public void init() throws Exception {
SpringApplicationBuilder builder = new SpringApplicationBuilder(MyJavaFXApplication.class);
builder.headless(false); // Needed for TestFX
context = builder.run(getParameters().getRaw().stream().toArray(String[]::new));
FXMLLoader loader = new FXMLLoader(getClass().getResource("main.fxml"));
loader.setControllerFactory(context::getBean);
rootNode = loader.load();
}
I now want to test this JavaFX application, for this I use:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class MyJavaFXApplicationUITest extends TestFXBase {
#MockBean
private MachineService machineService;
#Test
public void test() throws InterruptedException {
WaitForAsyncUtils.waitForFxEvents();
verifyThat("#statusText", (Text text ) -> text.getText().equals("Machine stopped"));
clickOn("#startMachineButton");
verifyThat("#startMachineButton", Node::isDisabled);
verifyThat("#statusText", (Text text ) -> text.getText().equals("Machine started"));
}
}
This starts a Spring context and replaces the "normal" beans with the mock beans as expected.
However, I now get a java.awt.HeadlessException because this 'headless' property is not set to false like is done during normal startup. How to I set this property during the test?
EDIT:
Looking closer it seems that there are 2 context started, one that the Spring testing framework starts and the one I create manually in the init method, so the application under test is not using the mocked beans. If somebody would have a clue how to get the test context reference in the init() method, I would be very happy.

The #SpringBootTest uses SpringBootContextLoader class as context loader, so the ApplicationContext is load from the method SpringBootContextLoader.loadContext as this:
SpringApplication application = getSpringApplication();
......
return application.run();
When invoke the method application.run(), application configure the system headless property with it's internal headless property.
So, If we want to set 'headless' property in a Spring Boot test, just create a customize specific ContextLoader class extends SpringBootContextLoader class, and override the method getSpringApplication with set the headless property as false, then assign the specific ContextLoader with annotation #ContextConfiguration for #SpringBootTest. The code:
public class HeadlessSpringBootContextLoader extends SpringBootContextLoader {
#Override
protected SpringApplication getSpringApplication() {
SpringApplication application = super.getSpringApplication();
application.setHeadless(false);
return application;
}
}
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment=SpringBootTest.WebEnvironment.NONE)
#ContextConfiguration(loader = HeadlessSpringBootContextLoader.class)
public class ApplicationTests {
#Test
public void contextLoads() {
}
}

The comment from Praveen Kumar pointed in the good direction. When I run the test with -Djava.awt.headless=false, then there is no exception.
To solve the other problem of the 2 Spring contexts, I had to do the following:
Suppose this is your main JavaFx startup class:
#SpringBootApplication
public class MyJavaFXClientApplication extends Application {
private ConfigurableApplicationContext context;
private Parent rootNode;
#Override
public void init() throws Exception {
SpringApplicationBuilder builder = new SpringApplicationBuilder(MyJavaFXClientApplication.class);
builder.headless(false); // Needed for TestFX
context = builder.run(getParameters().getRaw().stream().toArray(String[]::new));
FXMLLoader loader = new FXMLLoader(getClass().getResource("main.fxml"));
loader.setControllerFactory(context::getBean);
rootNode = loader.load();
}
#Override
public void start(Stage primaryStage) throws Exception {
Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds();
double width = visualBounds.getWidth();
double height = visualBounds.getHeight();
primaryStage.setScene(new Scene(rootNode, width, height));
primaryStage.centerOnScreen();
primaryStage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void stop() throws Exception {
context.close();
}
public void setContext(ConfigurableApplicationContext context) {
this.context = context;
}
}
And for testing, you use this abstract base class (Courtesy of this YouTube video by MVP Java):
public abstract class TestFXBase extends ApplicationTest {
#BeforeClass
public static void setupHeadlessMode() {
if (Boolean.getBoolean("headless")) {
System.setProperty("testfx.robot", "glass");
System.setProperty("testfx.headless", "true");
System.setProperty("prism.order", "sw");
System.setProperty("prism.text", "t2k");
System.setProperty("java.awt.headless", "true");
}
}
#After
public void afterEachTest() throws TimeoutException {
FxToolkit.hideStage();
release(new KeyCode[0]);
release(new MouseButton[0]);
}
#SuppressWarnings("unchecked")
public <T extends Node> T find(String query, Class<T> clazz) {
return (T) lookup(query).queryAll().iterator().next();
}
}
Then you can write a test like this:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class MyJavaFXApplicationUITest extends TestFXBase {
#MockBean
private TemperatureService temperatureService;
#Autowired
private ConfigurableApplicationContext context;
#Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("main.fxml"));
loader.setControllerFactory(context::getBean);
Parent rootNode = loader.load();
stage.setScene(new Scene(rootNode, 800, 600));
stage.centerOnScreen();
stage.show();
}
#Test
public void testTemperatureReading() throws InterruptedException {
when(temperatureService.getCurrentTemperature()).thenReturn(new Temperature(25.0));
WaitForAsyncUtils.waitForFxEvents();
assertThat(find("#temperatureText", Text.class).getText()).isEqualTo("25.00 C");
}
}
This allows to start the UI using mock services.

Related

how to configure javafx with springboot with more than 1 window?

I have problem.
This is my main:
#SpringBootApplication
public class MyAppSpringApplication extends Application {
public static ConfigurableApplicationContext springContext;
private FXMLLoader fxmlLoader;
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage stage) throws Exception {
fxmlLoader.setLocation(getClass().getResource("/sample.fxml"));
Parent root = fxmlLoader.load();
stage.setTitle("Sample app");
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
#Override
public void stop() throws Exception {
springContext.stop();
}
#Override
public void init() throws Exception {
springContext = SpringApplication.run(MyAppSpringApplication.class);
fxmlLoader = new FXMLLoader();
fxmlLoader.setControllerFactory(springContext::getBean);
}
}
And my first window (sample.fxml) with samplecontroller and sampleservice works ok. But i create another dish-builder.fxml with their contoller and service, but when i try to use my service there, it doesnt work because of null in dishbuilderservice (albo doesnt work sampleservice in that new controller). I heard that i shound also use that:
public static ConfigurableApplicationContext springContext;
but i have no idea how should i use it. Sorry for my weak knowledge and english.
#Controller
public class DishBuilderController implements Initializable {
#Autowired
DishBuilderService dishBuilderService;
#Autowired
SampleService sampleService;
private void somefun(){
sampleService.somefunInService(); //here sampleService and
every other service has null.
}
Here is the moment when i open new dishBuilder window (its in SampleController):
#FXML
void addNoweOknoClicked(ActionEvent event) {
try {
Stage stage = (Stage)anchorPane.getScene().getWindow();
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("/dish-builder.fxml"));
AnchorPane root = fxmlLoader.load();
stage.setTitle("Sample app");
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}catch (IOException e){
e.printStackTrace();
}
}
When you load dish-builder.fxml you are not setting the controller factory on the FXMLLoader. This means the FXMLLoader is simply creating the controller by calling its no-arg constructor. Since the controller is not a spring-managed bean, spring cannot inject any components into it.
You need to set the controller factory, as you do when you load sample.fxml, so that the FXMLLoader will ask Spring to retrieve the controller from the application context.
A couple of points that are not strictly relevant to your question:
There is no need to expose the ApplicationContext as a public static field. You can inject it into any spring-managed beans that need access to it
It is not recommended to re-use FXMLLoaders. Therefore there's no point in making the FXMLLoader an instance variable.
The #Controller annotation is intended for web controllers in a Spring MVC application. These are quite different to controllers in the JavaFX sense. You should use a generic #Component annotation for JavaFX controllers.
In the event that you were to reload an FXML file, you would need a new controller instance. This means that if the controller is managed by Spring, it needs to have PROTOTYPE scope, instead of the default SINGLETON scope.
So you need:
#SpringBootApplication
public class MyAppSpringApplication extends Application {
private ConfigurableApplicationContext springContext;
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage stage) throws Exception {
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setControllerFactory(springContext::getBean);
fxmlLoader.setLocation(getClass().getResource("/sample.fxml"));
Parent root = fxmlLoader.load();
stage.setTitle("Sample app");
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
#Override
public void stop() throws Exception {
springContext.stop();
}
#Override
public void init() throws Exception {
springContext = SpringApplication.run(MyAppSpringApplication.class);
}
}
Then your SampleController should look like
#Component
#Scope("prototype")
public class SampleController {
#Autowired
private ConfigurableApplicationContext springContext ;
#FXML
void addNoweOknoClicked(ActionEvent event) {
try {
Stage stage = (Stage)anchorPane.getScene().getWindow();
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setControllerFactory(springContext::getBean);
fxmlLoader.setLocation(getClass().getResource("/dish-builder.fxml"));
AnchorPane root = fxmlLoader.load();
stage.setTitle("Sample app");
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}catch (IOException e){
e.printStackTrace();
}
}
}
and similarly
#Component
#Scope("prototype")
public class DishBuilderController implements Initializable {
#Autowired
DishBuilderService dishBuilderService;
#Autowired
SampleService sampleService;
private void somefun(){
// this should now work, since the controller is managed by Spring:
sampleService.somefunInService();
}
}

Junit 5, Spring Application Context does not close on #DirtiesContext

My application context is not closed after test method.
I use Junit 5.3.1, spring 5.1.0.RELEASE for Selenium WebDriver tests.
This is my bean:
#Configuration
public class WebDriverConfig {
// ... Some Code ...
#Bean(destroyMethod = "quit")
#Primary
public DelegatingWebDriver cleanWebDriver(WebDriver driver) throws Exception {
driver.manage().deleteAllCookies();
driver.manage().window().maximize();
return new DelegatingWebDriver(driver);
}
// ... Some more code ...
}
This is my class:
#ExtendWith({SpringExtension.class})
#ContextConfiguration(classes = { WebDriverConfig.class, LoggerConfig.class, EmailConfig.class})
#TestExecutionListeners(listeners= {ScreenshotTaker.class, DependencyInjectionTestExecutionListener.class, TestListener.class})
#DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
public class BasicScenariosIT {
#Inject
private DelegatingWebDriver driver;
#SuppressWarnings("unused")
#Inject
private URI baseUrl;
#Inject
private Logger logger;
private DelegatingExtentTest testCase;
// ... Some tests ...
}
I expect the line:
#DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
to close the application context and fire up the line:
#Bean(destroyMethod = "quit")
In my case, call method "quit" close the browser and start a new one. However it doesn't seem to happen.
Would appreciate the help
Well, I found a workaround.
I implemented spring test listeners, and in the listener I marked the context as dirty, instead of relying on the #DirtiesContext annotation.
The listener looks like this:
#ExtendWith({SpringExtension.class})
#ContextConfiguration(classes = { WebDriverConfig.class, LoggerConfig.class})
#TestExecutionListeners(listeners= {DependencyInjectionTestExecutionListener.class})
public class RunnerExtension extends AbstractTestExecutionListener {
#Autowired
protected Logger logger;
#Autowired
protected DelegatingWebDriver driver;
#Override
public void beforeTestClass(TestContext testContext) throws Exception {
testContext.getApplicationContext()
.getAutowireCapableBeanFactory()
.autowireBean(this);
}
#Override
public void beforeTestMethod(TestContext testContext) throws Exception {
// .. some code here ..
}
#Override
public void beforeTestExecution(TestContext testContext) throws Exception {
// .. some code here ..
}
#Override
public void afterTestExecution(TestContext testContext) throws Exception {
// .. some code here ..
}
#Override
public void afterTestMethod(TestContext testContext) throws IOException {
// .. some code here ..
testContext.markApplicationContextDirty(HierarchyMode.EXHAUSTIVE);
}
#Override
public void afterTestClass(TestContext testContext) throws IOException {
// .. some code here ..
}
}
The important code line is:
testContext.markApplicationContextDirty(HierarchyMode.EXHAUSTIVE);
It marks the context as dirty and a new context will be created in the next session.

Apache camel Junit mock issue

I am writing a JUnit test case for a Route class. I'm facing a problem while mocking ServiceClass inside the Processor class.
public class SaveRouteTest extends CamelTestSupport {
private Exchange exchange;
protected ProducerTemplate template;
private SaveRequestBuilder saveRequestBuilder;
private SaveRoute route;
private SaveProcessor saveProcessor;
private ApplicationContext springContext = createApplicationContext();
#Mock
SaveServiceClient saveServiceClient;//Not able to mock this class
#BeforeClass
public void prepareTestCamelContext() throws Exception {
route = springContext.getBean("saveRoute", saveRoute.class);
saveProcessor = springContext.getBean("saveProcessor",
SaveProcessor.class);
saveRequestBuilder = springContext.getBean("saveRequestBuilder",
SaveRequestBuilder.class);
}
#BeforeMethod
public void init() throws SQLException, ServiceException {
MockitoAnnotations.initMocks(this);
exchange = new DefaultExchange(context);
}
#Override
protected RouteBuilder[] createRouteBuilders() throws Exception {
template = context.createProducerTemplate();
return new RouteBuilder[]{route};
}
#Test
public void testHotelCommitTransactionRouteSuccessReturn() throws
Exception {
when(saveServiceClient.invokeServiceWithName(anyObject()).
thenReturn("Response");
exchange.getIn().setBody("Request detail");
exchange = template.send("direct:SaveRoute",exchange);
}
protected ApplicationContext createApplicationContext() {
return new ClassPathXmlApplicationContext("classpath*:config/spring/testContext.xml");
}
}
#Component
public class SaveRoute extends SPRouteBuilder {
#Autowired
private SaveProcessor saveProcessor;
#Override
public void configure() throws Exception {
from("direct:SaveRoute")
.routeId("save")
.to("direct:ProcessSaveFlow")
.end();
from("direct:ProcessSaveFlow")
.process(saveProcessor)
.end();
}
}
public class SaveProcessor implements Processor {
#Autowired
SaveServiceClient saveServiceClient;
#Override
public void process(Exchange exchange) throws Exception {
//This line of code not able to mock
String response = saveServiceClient.invokeServiceWithName(exchange);
exchange.getIn().setBody(response);
}
}
How to resolve mocking of saveServiceClient.invokeServiceWithName? The debugger is always going inside this method. I tried using both mock objects and an injected mock. I can't make the method call directly.
You are creating a mock object, however you are not injecting it anywhere (normally you are doing it with #InjectMocks annotation - read about it).
I think there are several possibilities:
Provide a #MockBean object, which will be considered as a bean candidate in context.
There is a code example for mocking beans.
#RunWith ( CamelSpringRunner.class )
#SpringBootTest
public class RouteBuilderTest extends CamelSpringTestSupport {
#Autowired
private ApplicationContext applicationContext;
#MockBean
private ServiceClient serviceClient;
#Override
public void setUp() throws Exception {
MockitoAnnotations.initMocks( this );
super.setUp();
}
#Override
public void tearDown() {
}
#Test
public void test() {
when( serviceClient.doStuff() ).thenReturn( "mockedResponse" );
}
}
Mock SaveProcessor and inject it to Route class - you shouldn't take care of ServiceClient, because you are trying to test too much. Tests for SaveProcessor should be separated, tests for route don't need this logic.

JavaFX & Spring Boot - NPE

I'm still fighting with my issue. I want to use Spring Framework in order to incject dependencies and I have to use Spring boot to integrate both.
Unfortunately, in first view autowiring is run correctly, but if I go next Stage, I got still only Null Pointer Exception.
Thats main class:
#SpringBootApplication(scanBasePackages = "boxingchallenge")
public class BoxingChallengeApplication extends Application {
public ConfigurableApplicationContext springContext;
private Parent root;
public static Stage stage;
#Override
public void init() throws Exception {
springContext = SpringApplication.run(BoxingChallengeApplication.class);
springContext.getAutowireCapableBeanFactory().autowireBean(this);
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/FXML/start.fxml"));
fxmlLoader.setControllerFactory(springContext::getBean);
root = fxmlLoader.load();
}
#Override
public void start(Stage primaryStage) throws Exception {
stage = primaryStage;
primaryStage.setTitle("Boxing challenge");
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
#Override
public void stop() {
springContext.stop();
}
public static void main(String[] args) {
launch(BoxingChallengeApplication.class, args);
}
}
Here in first controller class autowiring run cool:
#Component
public class Start {
#FXML
public Button loadGame;
#FXML
public Button create;
#Autowired
private Boxer boxer;
public void load(ActionEvent event) {
System.out.println(boxer.getName());
}
//next stage
public void createNew(ActionEvent event) throws IOException {
Parent root = FXMLLoader.load(getClass().getResource("/FXML/creator.fxml"));
BoxingChallengeApplication.stage.setScene(new Scene(root));
}
}
Here in second stage, autowiring not working:
#Component
public class Creator {
#FXML
public Button ready;
public TextField nation;
public TextField name;
public Boxer boxer;
/*#Autowired
private ApplicationContext context;*/
#Autowired
public void setBoxer(Boxer boxer) {
this.boxer = boxer;
}
public void createdAndPlay(ActionEvent event) {
if (boxer == null)
System.out.println("boxer is null");
else
System.out.println("Injected correctly");
}
}
Thanks, i hope it's going to finished...
#Jewelsea's comment is correct: you must set the controller factory when you load creator.fxml. If you don't do this, the FXMLLoader will create the controller simply by calling its no-arg constructor, so Spring will know nothing about it and will have no opportunity to inject any dependencies.
To do this, all you need is access to the ApplicationContext in Start, and you can inject "well-known objects", of which the ApplicationContext is an example, into your Spring-managed beans:
#Component
public class Start {
#FXML
public Button loadGame;
#FXML
public Button create;
#Autowired
private Boxer boxer;
#Autowired
private ApplicationContext context ;
public void load(ActionEvent event) {
System.out.println(boxer.getName());
}
//next stage
public void createNew(ActionEvent event) throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/FXML/creator.fxml"));
load.setControllerFactory(context::getBean);
Parent root = loader.load();
BoxingChallengeApplication.stage.setScene(new Scene(root));
}
}
As an aside, you almost certainly want a new instance of any controller when you load an FXML file, so you should probably make any controllers prototype scope.

EJB embedded container - dependency injection don't work?

Have a look at following code:
#Local
public interface MyService {
void printMessage();
}
#Stateless
public class MyServiceImpl implements MyService {
#Override
public void printMessage() {
System.out.println("Hello from MyService.");
}
}
#Stateless
#Named
public class Application {
#EJB
public MyService sampleService;
private static Application getApplication() throws NamingException {
Properties properties = new Properties();
properties.setProperty(EJBContainer.APP_NAME, "admin");
EJBContainer.createEJBContainer(properties); //.getContext();
Context context = new InitialContext();
Application application = (Application) context.lookup("java:global/admin/classes/Application");
return application;
}
public static void main(String[] args) throws NamingException {
Application application = getApplication();
application.start(args);
}
private void start(String[] args) {
sampleService.printMessage();
}
}
I expected to have simpletService instance available at start() operation, but it is equal to null. All classes are part of one project (placed in separated files). Where I have made mistake? Thanks for advice.
Finally I have found solution. When I changed start() operation to be public and injection started work fine.

Categories