Top 7 JavaFX Testing Mistakes You Need To Avoid!

 

Transcript:

No more trial and error approach when testing JavaFX. Here are top seven mistakes when testing JavaFX applications and ways to avoid them using the test fix framework. Mistake number one, updating the UI of the fixed thread. JavaFX creates an application thread when the application starts and only this thread can render the UI elements. But the tests run on the GUI thread and so if you try to perform the UI actions, it may result in illegal state exception, random null pointer exceptions or deadlocks. So what can we do right to the UI to mutate controls on the fixed thread? If you use the aix robot class, you can wrap the interactions using the robot interact method. read from the UI to get text snapshot pixels or query the layout also on the fixed thread and return a value.

Mistake number two, incorrect bootstrapping when using spring boot. Test a fix owns the stage, spring boot owns the beans. So if you boot spring without giving it the testix stage, the beans won't be able to use it. Plus, if you use FXML, FXML loader uses a different class loader from that of Spring. Controllers that Spring create aren't of the same type as the FXML asks for. So, what can we do? Make FXML loader use the same class loader as Spring in the application code. Use the start annotation to wire up the real stage and dependency injection to inject fakes. Don't call new FXML application start if uh this code boots spring internally.

Mistake number three confusing handler wiring with the real user input. If you call the controller methods directly in the UI tests, you are testing the code wiring not the real event path. As a result, your tests may pass but miss important box or the tests may hang and fail because the input never fired. What can we do? Well, obviously avoid calling the controller methods directly in the UI tests. Use robot click on or similar methods to test user interaction and UI behavior such as pressed or visuals and so on. Use button fire or similar control methods to assert handler effect. In the headless mode, if the platform can do something like going full screen, assert proxy signal like pseudo classes or button state.

Mistake number four, bracing the fix event Q. JavaFX is a single thread kit. So the UI events get cued. If you assert in tests before the queue is drained, you are testing the UI that doesn't exist yet. Therefore, tests pass or fail unpredictably depending on the TI CPU moon cycles or what not. What can we do in the case of simple changes? You can use the uh wait for a sync utils class and its method wait for fix events. This method helps to wait before the event Q of the UI is trained. In the case you are waiting for observed outcomes, you can use the wait for method to wait for some condition to be met.

Mistake number five, assuming pixel perfect quality across platforms. The pixel colors of JavaFX can differ slightly on various platforms due to various reasons. If the tests assert the exact RGB equality on all platforms, the tests may pass locally and fail in CI or on another machine. So what can we do? Don't assert the exact color. Compare baseline to the change state and allow for some tolerance in color and pixel density. In addition, we can allow for a small absolute difference when comparing colors. sample pixels inside the shape, not near borders. This will help to avoid having different colors if the borders uh blend with the background.

Mistake number six, misconfiguring headless CI. Running JavaFX tests in CI differs from the standard testing process. These tests must run in headless state and be backed by monocle. But it is not enough to simply wire the monocle. Tests that pass locally may fail in CI due to multiple reasons. UI tests that run in parallel. Required modules are locked down. Tests assert platform features that don't exist in headless mode like full screen. So what can we do? Well, first thing first, add the dependency for the monocle. Then specify all required modules in a separate plug-in with the add opens flag.

The demo applications that I use in this video were developed and compiled NCI using uh Liberica JDK bundled with JavaX. So there was no need to add any extra dependencies on JavaFX. But if you add the dependencies on JavaFX manually, you may have to use the additional add exports flag. It allows compile time access to Glass internals. Then in the workflow file, make sure to install all necessary native libraries for JavaFX. Then run the tests with the correct profile. Don't forget to install Java or add the dependencies on Javaix or you can install the JDK with Javaix bundled already.

So for instance, the file on the screen installs Libera JDK 21 with Javaix in the setup Java action. Mistake number seven, entangling business logic with the UI. Last but not least, testing business logic with the UI is not the best practice. Just as you separate controller and service methods uh in tests for web apps, you should separate testing the business logic and the UI in JavaSix applications. The domain logic tests should not coexist with the UI test in one class. So what can we do? The best solution would be to move the business logic to a separate view and test it with a plain JUnit tests. 

Summary

This video explains the seven most common mistakes when testing JavaFX applications with TestFX and how to avoid them. It covers errors such as updating the UI outside the FX thread, incorrect Spring Boot integration, calling controller methods directly instead of simulating real user input, racing the JavaFX event queue, relying on pixel-perfect comparisons across platforms, misconfiguring headless CI with Monocle, and mixing business logic tests with UI tests. For each mistake, the video provides practical solutions like using interact(), unifying class loaders for FXML, using robot actions instead of direct method calls, waiting for the event queue to drain, allowing color tolerance in pixel tests, properly setting up CI with required modules, and separating domain logic into plain JUnit tests.

About Catherine

Java developer passionate about Spring Boot. Writer. Developer Advocate at BellSoft

Social Media

Videos
card image
Apr 2, 2026
Java Memory Options You Need in Production

JVM memory tuning can be tricky. Teams increase -Xmx and assume the problem is solved. Then the app still hits OOM. Because maximum heap size is not the only thing that affects memory footprint. The JVM uses RAM for much more than heap: metaspace, thread stacks, JIT/code cache, direct buffers, and native allocations. That’s why your process can run out of memory while heap still looks “fine”. In this video, we break down how JVM memory actually works and how to control it with a minimal, production-safe set of flags. We cover heap sizing (-Xms, -Xmx), dynamic resizing, direct memory (-XX:MaxDirectMemorySize), and total RAM limits (-XX:MaxRAMPercentage) — especially in containerized environments like Docker and Kubernetes. We also explain GC choices such as G1, ZGC, and Shenandoah, when defaults are enough, and why GC logging (-Xlog:gc*) is mandatory before tuning. Finally, we show how to diagnose failures with heap dumps and OOM hooks. This is not about adding more flags. It’s about understanding what actually consumes memory — and making decisions you can justify in production.

Videos
card image
Mar 26, 2026
Java Developer Roadmap 2026: From Basics to Production

Most Java roadmaps teach tools. This one teaches order — the only thing that actually gets you to production. You don’t need to learn everything. You need to learn the right things, in the right sequence. In this video, we break down a practical Java developer roadmap for 2026 — from syntax and OOP to Spring, databases, testing, and deployment. Structured into 8 levels, it shows how real engineers grow from fundamentals to production-ready systems. We cover what to learn and what to ignore: core Java, collections, streams, build tools, Git, SQL and JDBC before Hibernate, the Spring ecosystem, testing with JUnit, and deployment with Docker and CI/CD. You’ll also understand why most developers get stuck — jumping into frameworks too early, skipping SQL, or treating tools as knowledge. This roadmap gives you a clear path into real-world Java development — with priorities, trade-offs, and production context.

Further watching

Videos
card image
Apr 30, 2026
Java Flight Recorder Tutorial: How to Profile Java Applications

High CPU, GC spikes, or slow startup are common production issues, but logs and metrics don’t always reveal what the JVM is actually doing. Java Flight Recorder (JFR) provides a precise, low-overhead view of JVM behavior, safe for use even in production environments. In this video, you’ll learn how to use JFR to identify real bottlenecks such as CPU hotspots, memory allocation pressure, thread contention, and I/O stalls. We walk through the full workflow, including starting recordings with JVM flags, controlling them via jcmd, running JFR inside Docker containers, and attaching to live systems using ephemeral containers. Then we analyze a real Spring Boot recording in JDK Mission Control, breaking down GC behavior, allocation patterns, thread states, and method-level hotspots. If you want to move from symptoms to root cause with more confidence, this approach will help. Full article with commands and examples: [https://bell-sw.com/blog/how-to-profile-java-applications-with-jfr-beginner-s-guide/](https://bell-sw.com/blog/how-to-profile-java-applications-with-jfr-beginner-s-guide/)

Videos
card image
Apr 22, 2026
Dynamic SQL Queries with Spring Data JPA in 6 Minutes

If your repository layer has multiple queries for different filter combinations, your data access logic is already getting harder to maintain. In this video, we implement dynamic SQL queries in Spring Data JPA using Specifications — a composable approach that helps avoid query duplication and keeps your filtering logic clean. We build a flexible filtering system with optional parameters (category, language, format, price) and show how Specification.unrestricted() skips empty filters, while Specification.allOf(...) combines them into a single query. We also address a common issue: string-based field access. It’s fragile and can break at runtime when your model changes. Using the JPA Static Metamodel, we move to compile-time safety. The result is a cleaner, more maintainable way to implement dynamic filtering in Spring-based applications.

Videos
card image
Apr 8, 2026
Best Oracle Java Alternatives in 2026 Comparison of OpenJDK Distributions

A comparison of major OpenJDK distributions (Temurin, Liberica, Zulu, Corretto, Semeru, etc.), covering who maintains them, how updates are delivered, and what lifecycle guarantees they provide. We also explain why upstream OpenJDK isn’t production-ready and how your vendor choice impacts real-world systems. Useful for Spring Boot, containers, and Kubernetes to avoid hidden risks and choose the right runtime.