Good day! A year and a half ago, my team had to test a Java Swing application that could have different visualizations strung on a common process. There were few articles on this topic at that time; there were no specific solutions at all. TestComplete and other scripting technologies (yes, TestComplete supporters will forgive me) did not want to use it, because the application must have a flexible architecture that can be expanded and modified as part of the Agile process.
A day of Google search, analysis of dozens of examples and technologies led me to two possible options:
Fest
Jemmy
Without plunging into the depths of the depths of comparison, I chose the Fest library. With its help and, of course, Junit, Mockito, we started testing our application. About this and tell below. Of course, you can start by reading the documentation from Getting Started, and use the work through FrameFixture - everything is well described here , but if tasks go beyond searching / clicking on standard Java Swing components, then below, using specific tasks as an example, I will show how it's done. ')
Let's start with the robot, namely org.fest.swing.core.Robot:
The robot can click components, focus on them, work with the mouse cursor, perform various checks. But we will start with the implementation of ComponentFinder, which the robot kindly provides us. And so that it is interesting, we will look at everything with an example. In this article, I will not propose architectural solutions, propose an implementation of the PageObject pattern for Swing, - rather, I will try to just explain with an example how to use the library. You can already invent your own architecture - tasks are always different.
Suppose we have a JFrame with three custom buttons. Suppose you want to find them, check that the texts on them are “Buy”, “Sell”, “Delete”, respectively, and that the “Delete” button! IsEnabled ().
For this we need:
ComponentFinder finder = robot.finder();
It has many methods, such as findByLabel, findByName. But we will turn our attention to:
finder.find (ComponentMatcher matcher) - returns the Component
finder.findAll (ComponentMatcher matcher) returns a Collection.
There are options for methods 1 and 2 with generic matcher passing - sometimes it is useful. But since we usually know what we are looking for, 1 and 2 are mainly used.
The ComponentMatcher interface is essentially one method:
booleanmatches(Component c)
We will need an implementation to search our custom button for text on it:
If it is not found, it will return a ComponentLookupException. We can also find the second button and the third one by changing the expectedCaption of the matcher. With the third button, you can do differently. If you often have to check the buttons “zadizabilnost”, then you should overload the CaptionMatcher constructor and add to enable / disable check in matches.
It was possible to go the other way - to make ClassMatcher, in which matches returns true, if the component of the instanceof CustomButton. And search using findAll. Then we would get all the buttons and then, running through them, would lead to the class of our buttons and check everything that is needed.
At this point, one remark is absolutely necessary - nobody canceled threads on Swing rendering, and if you start searching for a component before it has been rendered, you will receive a ComponentLookupException. Therefore, the best practice, in my opinion, is to wrap the search for a component in a loop and look for a component with a certain timeout, intercepting all the search errors. If a component was not found for the selected timeout, then assume that it is not there and throw a ComponentLookupException above.
We learned to find components. Now about the clicks. Experience has shown that it is better to throw an ActionEvent directly to a component than to try using a robot to click with the mouse. First, with this approach, you can safely continue to work while chasing tests, and second, to quit the event faster than clicking. We have this event implemented at the level of the component itself - decorated in the form of the public method doClick ().
To demonstrate the Fest in action:
1. Below is a link to turnips - for clarity, I sketched out a few tests that check addition, subtraction, and multiplication in a calculator. Please note that between the clicks inserted Thread.sleep (100); In order to speed up, it is better to somehow form the “expected” result of the action. Not only in the calculator, but also in any other project - it is desirable to associate any action with the expected result.
2. And here is a video of how our product is automatically tested - the self-checkout checkout . Everything is built on actions and expected results. Some screens and components you do not even have time to see - nevertheless, they manage to be checked along and across: