{"id":257375,"date":"2015-05-18T11:33:02","date_gmt":"2015-05-18T07:33:02","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=257375"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=257375","title":{"rendered":"\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 JavaFX \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439"},"content":{"rendered":"<p>     \t<img decoding=\"async\" src=\"\/\/habrastorage.org\/files\/864\/62a\/07a\/86462a07a43947d49b2226fd453807c1.jpg\"\/><\/p>\n<p>  \u0414\u043e\u0431\u0440\u044b\u0439 \u0434\u0435\u043d\u044c!<\/p>\n<p>  \u0412 \u043c\u0438\u0440\u0435, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0438 \u043d\u0430 \u044d\u0442\u0430\u043f\u0435 \u0432\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u044f \u043f\u0440\u0435\u0432\u044b\u0448\u0430\u0435\u0442 \u0432 \u0441\u043e\u0442\u043d\u0438 \u0438 \u0442\u044b\u0441\u044f\u0447\u0438 \u0440\u0430\u0437 \u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u044c \u0438\u0441\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043d\u0430 \u044d\u0442\u0430\u043f\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438, \u043d\u0443\u0436\u043d\u043e \u0432\u0441\u0435\u0433\u0434\u0430 \u0438\u0441\u043a\u0430\u0442\u044c \u043e\u0442\u0432\u0435\u0442 \u043d\u0430 \u0432\u043e\u043f\u0440\u043e\u0441: \u00ab\u0430 \u043a\u0430\u043a \u044d\u0442\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438?\u00bb \u0412\u043e\u043f\u0440\u043e\u0441\u044b \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f JavaFX \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u0430\u044f \u043f\u0430\u0443\u0442\u0438\u043d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0435 \u043e\u0441\u0432\u0435\u0449\u0430\u0435\u0442. \u041d\u043e \u0432\u0441\u0451 \u0436\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043d\u0430\u0439\u0442\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0445 \u0438\u0434\u0435\u0439, \u0438 \u044f \u0445\u043e\u0447\u0443 \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u0441 \u0432\u0430\u043c\u0438 \u0441\u0432\u043e\u0438\u043c\u0438 \u043d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u044f\u043c\u0438.<\/p>\n<p>  \u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u043a\u0430\u043a \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u044c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u043d\u0430 JavaFX \u0444\u043e\u0440\u043c\u0435, \u043a\u0430\u043a \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0438\u0445 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430, \u043a\u0430\u043a \u043a\u043b\u0438\u043a\u0430\u0442\u044c \u043d\u0430 \u043d\u0438\u0445 \u0438 \u0442\u0430\u043a \u0434\u0430\u043b\u0435\u0435. \u042d\u0442\u043e \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0439 \u043d\u0430\u0431\u043e\u0440 \u0432\u0445\u043e\u0434\u043d\u044b\u0445 \u0442\u043e\u0447\u0435\u043a \u0432 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f JavaFX \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439.<a name=\"habracut\"><\/a><\/p>\n<p>  <\/p>\n<h4>1. \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 <\/h4>\n<p>  \u041d\u0430\u0431\u043e\u0440 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a: guava, testFx, hamcrest \u0438 JUnit.<br \/>  \u042f \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0438\u0430\u043b\u044c\u043d\u043e \u043d\u0435 \u0431\u0443\u0434\u0443 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u043b\u043e\u0433\u0438\u043a\u0443 \u0440\u0430\u0431\u043e\u0442\u044b \u0441\u0430\u043c\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0441\u043a\u0430\u0436\u0443 \u0442\u043e\u043b\u044c\u043a\u043e, \u0447\u0442\u043e \u044d\u0442\u043e \u043a\u0430\u043b\u044c\u043a\u0443\u043b\u044f\u0442\u043e\u0440, \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0439 \u043d\u0430 \u0441\u043a\u043e\u0440\u0443\u044e \u0440\u0443\u043a\u0443 \u2014 \u043f\u043e\u0441\u0442\u0430\u0440\u0430\u0435\u043c\u0441\u044f \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u0434\u043e\u043b\u0433\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u043d\u0438\u043c, \u043a\u0430\u043a \u0441 black-box. \u0422\u0435\u043c \u043d\u0435 \u043c\u0435\u043d\u0435\u0435 \u043d\u0430\u0447\u043d\u0443 \u044f \u0441 \u0441\u0430\u043c\u043e\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430 launcher-\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f:<\/p>\n<pre><code class=\"java\">public class CalculatorApp extends Application { \tprivate static Optional&lt;Callback&lt;Parent&gt;&gt; callback = Optional.empty();  \tpublic static void main(String[] args) { \t\tlaunch(args); \t}  \t@Override \tpublic void start(Stage primaryStage) throws Exception { \t\tBorderPane root = new BorderPane(); \t\troot.setCenter(new Calculator()); \t\tScene scene = new Scene(root); \t\tprimaryStage.setScene(scene); \t\tprimaryStage.show(); \t\tcallback.ifPresent(o -&gt; o.call(root)); \t} \t \tpublic static void onLoad(Callback&lt;Parent&gt; r) { \t\tCalculatorApp.callback = Optional.of(r); \t} } <\/code><\/pre>\n<p>  \u0417\u0430\u0447\u0435\u043c \u043d\u0443\u0436\u0435\u043d callback \u0441\u0442\u0430\u043d\u0435\u0442 \u043f\u043e\u043d\u044f\u0442\u043d\u043e \u0447\u0443\u0442\u044c \u043f\u043e\u0437\u0436\u0435. \u041f\u043e\u043a\u0430 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0437\u043d\u0430\u0442\u044c \u043e \u043d\u0451\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u044d\u0442\u043e:<\/p>\n<pre><code class=\"java\">public interface Callback&lt;T&gt; {     void call(T arg); } <\/code><\/pre>\n<p>  \u041f\u043e\u043c\u0438\u043c\u043e launcher-\u0430, \u043a\u0430\u043a \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0434\u043e\u0433\u0430\u0434\u0430\u0442\u044c\u0441\u044f, \u0435\u0441\u0442\u044c Calculator.java \u2014 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440, Calculator.fxml \u2014 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u0441\u043e \u0432\u0441\u0435\u0439 \u0438\u0435\u0440\u0430\u0440\u0445\u0438\u0435\u0439, layout-\u0430\u043c\u0438 \u0438 \u043f\u0440\u043e\u0447\u0438\u043c, Calculator.css \u2014 \u0441\u0442\u0438\u043b\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430\u043c\u0438 \u043d\u0430\u0448\u0435\u0439 \u0432\u0438\u0437\u0443\u0430\u043b\u043a\u0438. \u0412 \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u043c \u0441\u0447\u0451\u0442\u0435 \u043d\u0430\u0448 \u043a\u0430\u043b\u044c\u043a\u0443\u043b\u044f\u0442\u043e\u0440 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043a\u0430\u043a-\u0442\u043e \u0442\u0430\u043a:<\/p>\n<p>  <img decoding=\"async\" src=\"\/\/habrastorage.org\/files\/803\/b6c\/c9a\/803b6cc9a4f54ca59f367fddc1e79d0f.PNG\"\/><\/p>\n<p>  <\/p>\n<h4>2. \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0442\u0435\u0441\u0442\u0430 <\/h4>\n<p>  <\/p>\n<pre><code class=\"java\">public class FirstTest { \tprivate static GuiTest controller;\t  \t@BeforeClass \tpublic static void setUpClass() { \t\tCalculatorApp.onLoad(r -&gt; { \t\t\tcontroller = new GuiTest() { \t\t\t\t@Override \t\t\t\tprotected Parent getRootNode() { \t\t\t\t\treturn r; \t\t\t\t} \t\t\t}; \t\t});  \t\tFXTestUtils.launchApp(CalculatorApp.class); \t\ttry { \t\t\tThread.sleep(1000); \t\t} catch (InterruptedException e) { \t\t\te.printStackTrace(); \t\t} \t} ... <\/code><\/pre>\n<p>  \u0427\u0442\u043e\u0431\u044b \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c TestFX \u043d\u0430\u043c \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f GuiTest() \u2014 \u044d\u0442\u043e \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0442\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0439 \u0432 \u0441\u0435\u0431\u0435 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0445 \u043c\u0435\u0442\u043e\u0434\u043e\u0432. \u041e\u043d \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043e\u0442 \u043d\u0430\u0441 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 Parent getRootNode(). Callback \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442 \u0432 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e GuiTest \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0439 root. \u042d\u0442\u043e\u0433\u043e \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0445\u043e\u0434\u0438\u0442\u044c \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0432\u043d\u043e \u043f\u043e \u0438\u0435\u0440\u0430\u0440\u0445\u0438\u0438 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432, \u0447\u0442\u043e \u043d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435 TestFX \u0438 \u0434\u0435\u043b\u0430\u0435\u0442. \u041e\u0447\u0435\u043d\u044c \u0441\u043e\u0432\u0435\u0442\u0443\u044e \u0437\u0430\u0433\u043b\u044f\u043d\u0443\u0442\u044c \u0432 \u0438\u0441\u0445\u043e\u0434\u043d\u0438\u043a\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u2014 \u0442\u0430\u043c \u0435\u0441\u0442\u044c \u043c\u043d\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0433\u043e \u0438 \u0441\u0440\u0430\u0437\u0443 \u043f\u043e\u043d\u044f\u0442\u043d\u044b \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u044b \u0435\u0451 \u0440\u0430\u0431\u043e\u0442\u044b.<\/p>\n<pre><code class=\"java\">FXTestUtils.launchApp(CalculatorApp.class); <\/code><\/pre>\n<p>  \u0416\u0434\u0430\u0442\u044c \u043d\u0435 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u2014 \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u0443\u043c\u043d\u043e\u0435 \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u043d\u043e \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u044b \u0443 \u043c\u0435\u043d\u044f Thread.sleep(1000);<\/p>\n<p>  <\/p>\n<h4>3. \u041c\u0435\u0442\u043e\u0434\u044b <\/h4>\n<p>  \u0412 \u043f\u0435\u0440\u0432\u0443\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u043d\u0430\u0443\u0447\u0438\u0442\u044c \u043d\u0430\u0448 \u0434\u0432\u0438\u0436\u043e\u043a \u043d\u0430\u0436\u0438\u043c\u0430\u0442\u044c \u0423\u0414\u0410\u041b. \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0432 Before:<\/p>\n<pre><code class=\"java\">private void clear() { \tcontroller.click(&quot;\u0423\u0414\u0410\u041b.&quot;); } <\/code><\/pre>\n<p>  \u0414\u0430, \u0438\u043c\u0435\u043d\u043d\u043e \u0442\u0430\u043a \u043f\u0440\u043e\u0441\u0442\u043e \u2014 \u0438 \u044d\u0442\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d \u0438\u0437 \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u0432. \u041d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043f\u043b\u0430\u0432\u043d\u043e\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u0435 \u043c\u044b\u0448\u043a\u0438 \u0438 \u043a\u043b\u0438\u043a. \u0427\u0442\u043e\u0431\u044b \u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u043d\u0435\u043d\u0443\u0436\u043d\u043e\u0439 \u0442\u0440\u0430\u0442\u044b \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043d\u0430 \u043a\u0440\u0430\u0441\u0438\u0432\u043e\u0441\u0442\u0438 \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043a \u043f\u0440\u043e\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u043d\u0438\u044e \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u043d\u0443\u0436\u043d\u043e\u0439 \u043d\u043e\u0434\u0435 (\u043d\u043e \u044f \u043e\u0441\u0442\u0430\u0432\u043b\u044e \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u044b\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0432\u0430\u043c \u0432\u0438\u0434\u0435\u043e \u0432 \u0434\u0438\u043d\u0430\u043c\u0438\u043a\u0435). \u0410 \u043f\u0440\u043e\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u043d\u0438\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0434\u0435\u043b\u0430\u0435\u0442\u0441\u044f \u043a\u0430\u043a-\u0442\u043e \u0442\u0430\u043a:<\/p>\n<pre><code class=\"java\">Event.fireEvent(your_node, new MouseEvent(MouseEvent.MOUSE_CLICKED, 0, 0, 0, 0, MouseButton.PRIMARY, 1, true, true, true, true, true, true, true, true, true, true, null)); <\/code><\/pre>\n<p>  \u0418\u0442\u043e\u0433\u043e \u043c\u044b \u0438\u043c\u0435\u0435\u043c \u0442\u043e, \u0447\u0435\u0433\u043e \u0438 \u0434\u043e\u0431\u0438\u0432\u0430\u043b\u0438\u0441\u044c \u2014 \u043e\u0447\u0438\u0441\u0442\u043a\u0430 \u043f\u043e\u043b\u0435\u0439 \u043a\u0430\u043b\u044c\u043a\u0443\u043b\u044f\u0442\u043e\u0440\u0430 (\u0441\u0431\u0440\u043e\u0441), \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u043c \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u044c \u043f\u0435\u0440\u0435\u0434 \u043a\u0430\u0436\u0434\u044b\u043c \u0442\u0435\u0441\u0442\u043e\u043c: <\/p>\n<pre><code class=\"java\">@Before public void beforeTest() { \tclear(); } <\/code><\/pre>\n<p>  \u0410\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u043c\u0435\u0442\u043e\u0434, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0430\u043a\u043b\u0438\u043a\u0430\u0435\u0442 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e\u0435 \u0447\u0438\u0441\u043b\u043e \u043d\u0430 \u043a\u0430\u043b\u044c\u043a\u0443\u043b\u044f\u0442\u043e\u0440\u0435.<\/p>\n<pre><code class=\"java\">public void click(int digit) { \tString numStr = Integer.toString(digit); \tfor (int i = 0; i &lt; numStr.length(); i++) { \t\tcontroller.click(String.valueOf(numStr.charAt(i))); \t} } <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u044f \u043f\u043e\u043a\u0430\u0436\u0443 \u0431\u043e\u043b\u0435\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u043d\u0430\u0436\u0430\u0442\u0438\u0439 \u043d\u0430 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044b. \u0417\u0430\u0434\u0430\u0447\u0430 \u2014 \u043d\u0430\u0443\u0447\u0438\u0442\u044c\u0441\u044f \u043d\u0430\u0436\u0438\u043c\u0430\u0442\u044c \u043d\u0430 +,-,*,\/,=. \u0417\u0430\u0433\u043b\u044f\u043d\u0435\u043c \u0432 \u043d\u0430\u0448\u0443 fxml \u0438 \u043f\u043e\u0439\u043c\u0451\u043c, \u0430 \u0447\u0435\u043c \u0442\u0430\u043a\u0438\u043c \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u043c \u043e\u0442\u043b\u0438\u0447\u0430\u044e\u0442\u0441\u044f \u044d\u0442\u0438 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b.  <\/p>\n<pre><code class=\"html\">&lt;Label fx:id=&quot;eq&quot;... &lt;Label fx:id=&quot;divide&quot;... &lt;Label fx:id=&quot;multiply&quot;... &lt;Label fx:id=&quot;subtract&quot;... &lt;Label fx:id=&quot;add&quot;... <\/code><\/pre>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0421\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043f\u043e\u043b\u043d\u044b\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 Calculator.fxml<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"xml\">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;  &lt;?import java.net.*?&gt; &lt;?import javafx.scene.control.*?&gt; &lt;?import java.lang.*?&gt; &lt;?import javafx.scene.layout.*?&gt;  &lt;fx:root maxHeight=&quot;-Infinity&quot; maxWidth=&quot;-Infinity&quot; minHeight=&quot;-Infinity&quot; minWidth=&quot;-Infinity&quot; prefHeight=&quot;400.0&quot; prefWidth=&quot;600.0&quot; styleClass=&quot;root&quot; type=&quot;GridPane&quot; xmlns=&quot;http:\/\/javafx.com\/javafx\/8&quot; xmlns:fx=&quot;http:\/\/javafx.com\/fxml\/1&quot;&gt;   &lt;columnConstraints&gt;       &lt;ColumnConstraints hgrow=&quot;SOMETIMES&quot; minWidth=&quot;10.0&quot; percentWidth=&quot;27.0&quot; prefWidth=&quot;100.0&quot; \/&gt;       &lt;ColumnConstraints hgrow=&quot;SOMETIMES&quot; minWidth=&quot;10.0&quot; percentWidth=&quot;27.0&quot; prefWidth=&quot;100.0&quot; \/&gt;     &lt;ColumnConstraints hgrow=&quot;SOMETIMES&quot; minWidth=&quot;10.0&quot; percentWidth=&quot;27.0&quot; prefWidth=&quot;100.0&quot; \/&gt;     &lt;ColumnConstraints hgrow=&quot;SOMETIMES&quot; minWidth=&quot;10.0&quot; percentWidth=&quot;19.0&quot; prefWidth=&quot;100.0&quot; \/&gt;   &lt;\/columnConstraints&gt;   &lt;rowConstraints&gt;       &lt;RowConstraints minHeight=&quot;10.0&quot; percentHeight=&quot;25.0&quot; prefHeight=&quot;30.0&quot; vgrow=&quot;SOMETIMES&quot; \/&gt;       &lt;RowConstraints minHeight=&quot;10.0&quot; percentHeight=&quot;25.0&quot; prefHeight=&quot;30.0&quot; vgrow=&quot;SOMETIMES&quot; \/&gt;     &lt;RowConstraints minHeight=&quot;10.0&quot; percentHeight=&quot;25.0&quot; prefHeight=&quot;30.0&quot; vgrow=&quot;SOMETIMES&quot; \/&gt;     &lt;RowConstraints minHeight=&quot;10.0&quot; percentHeight=&quot;25.0&quot; prefHeight=&quot;30.0&quot; vgrow=&quot;SOMETIMES&quot; \/&gt;     &lt;RowConstraints minHeight=&quot;10.0&quot; percentHeight=&quot;25.0&quot; prefHeight=&quot;30.0&quot; vgrow=&quot;SOMETIMES&quot; \/&gt;   &lt;\/rowConstraints&gt;    &lt;children&gt;       &lt;StackPane maxHeight=&quot;1.7976931348623157E308&quot; maxWidth=&quot;1.7976931348623157E308&quot; GridPane.columnSpan=&quot;4&quot;&gt;          &lt;children&gt;             &lt;TextField fx:id=&quot;input&quot; alignment=&quot;CENTER_RIGHT&quot; focusTraversable=&quot;false&quot; maxHeight=&quot;1.7976931348623157E308&quot; maxWidth=&quot;1.7976931348623157E308&quot; styleClass=&quot;input&quot; text=&quot;0&quot; GridPane.columnSpan=&quot;4&quot; \/&gt;             &lt;Label fx:id=&quot;description&quot; styleClass=&quot;operation&quot; StackPane.alignment=&quot;BOTTOM_LEFT&quot; \/&gt;          &lt;\/children&gt;       &lt;\/StackPane&gt;       &lt;Label alignment=&quot;CENTER&quot; maxHeight=&quot;1.7976931348623157E308&quot; maxWidth=&quot;1.7976931348623157E308&quot; onMouseClicked=&quot;#handleClick&quot; text=&quot;3&quot; GridPane.columnIndex=&quot;2&quot; GridPane.rowIndex=&quot;3&quot; \/&gt;       &lt;Label alignment=&quot;CENTER&quot; maxHeight=&quot;1.7976931348623157E308&quot; maxWidth=&quot;1.7976931348623157E308&quot; onMouseClicked=&quot;#handleClick&quot; text=&quot;9&quot; GridPane.columnIndex=&quot;2&quot; GridPane.rowIndex=&quot;1&quot; \/&gt;       &lt;Label alignment=&quot;CENTER&quot; maxHeight=&quot;1.7976931348623157E308&quot; maxWidth=&quot;1.7976931348623157E308&quot; onMouseClicked=&quot;#handleClick&quot; text=&quot;2&quot; GridPane.columnIndex=&quot;1&quot; GridPane.rowIndex=&quot;3&quot; \/&gt;       &lt;Label alignment=&quot;CENTER&quot; maxHeight=&quot;1.7976931348623157E308&quot; maxWidth=&quot;1.7976931348623157E308&quot; onMouseClicked=&quot;#handleClick&quot; text=&quot;1&quot; GridPane.rowIndex=&quot;3&quot; \/&gt;       &lt;Label alignment=&quot;CENTER&quot; maxHeight=&quot;1.7976931348623157E308&quot; maxWidth=&quot;1.7976931348623157E308&quot; onMouseClicked=&quot;#handleClick&quot; text=&quot;5&quot; GridPane.columnIndex=&quot;1&quot; GridPane.rowIndex=&quot;2&quot; \/&gt;       &lt;Label alignment=&quot;CENTER&quot; maxHeight=&quot;1.7976931348623157E308&quot; maxWidth=&quot;1.7976931348623157E308&quot; onMouseClicked=&quot;#handleClick&quot; text=&quot;8&quot; GridPane.columnIndex=&quot;1&quot; GridPane.rowIndex=&quot;1&quot; \/&gt;       &lt;Label alignment=&quot;CENTER&quot; maxHeight=&quot;1.7976931348623157E308&quot; maxWidth=&quot;1.7976931348623157E308&quot; onMouseClicked=&quot;#handleClick&quot; text=&quot;4&quot; GridPane.rowIndex=&quot;2&quot; \/&gt;       &lt;Label alignment=&quot;CENTER&quot; maxHeight=&quot;1.7976931348623157E308&quot; maxWidth=&quot;1.7976931348623157E308&quot; onMouseClicked=&quot;#handleClick&quot; text=&quot;7&quot; GridPane.rowIndex=&quot;1&quot; \/&gt;       &lt;Label alignment=&quot;CENTER&quot; maxHeight=&quot;1.7976931348623157E308&quot; maxWidth=&quot;1.7976931348623157E308&quot; onMouseClicked=&quot;#handleClick&quot; text=&quot;,&quot; GridPane.rowIndex=&quot;4&quot; \/&gt;       &lt;Label fx:id=&quot;eq&quot; alignment=&quot;CENTER&quot; maxHeight=&quot;1.7976931348623157E308&quot; maxWidth=&quot;1.7976931348623157E308&quot; onMouseClicked=&quot;#handleEq&quot; text=&quot;=&quot; GridPane.columnIndex=&quot;2&quot; GridPane.rowIndex=&quot;4&quot; \/&gt;       &lt;Label alignment=&quot;CENTER&quot; maxHeight=&quot;1.7976931348623157E308&quot; maxWidth=&quot;1.7976931348623157E308&quot; onMouseClicked=&quot;#handleClick&quot; text=&quot;0&quot; GridPane.columnIndex=&quot;1&quot; GridPane.rowIndex=&quot;4&quot; \/&gt;       &lt;Label alignment=&quot;CENTER&quot; maxHeight=&quot;1.7976931348623157E308&quot; maxWidth=&quot;1.7976931348623157E308&quot; onMouseClicked=&quot;#handleClick&quot; text=&quot;6&quot; GridPane.columnIndex=&quot;2&quot; GridPane.rowIndex=&quot;2&quot; \/&gt;       &lt;GridPane styleClass=&quot;operations&quot; GridPane.columnIndex=&quot;3&quot; GridPane.rowIndex=&quot;1&quot; GridPane.rowSpan=&quot;4&quot;&gt;         &lt;columnConstraints&gt;           &lt;ColumnConstraints hgrow=&quot;SOMETIMES&quot; minWidth=&quot;10.0&quot; prefWidth=&quot;100.0&quot; \/&gt;         &lt;\/columnConstraints&gt;         &lt;rowConstraints&gt;           &lt;RowConstraints minHeight=&quot;10.0&quot; prefHeight=&quot;30.0&quot; vgrow=&quot;SOMETIMES&quot; \/&gt;             &lt;RowConstraints minHeight=&quot;10.0&quot; prefHeight=&quot;30.0&quot; vgrow=&quot;SOMETIMES&quot; \/&gt;           &lt;RowConstraints minHeight=&quot;10.0&quot; prefHeight=&quot;30.0&quot; vgrow=&quot;SOMETIMES&quot; \/&gt;             &lt;RowConstraints minHeight=&quot;10.0&quot; prefHeight=&quot;30.0&quot; vgrow=&quot;SOMETIMES&quot; \/&gt;           &lt;RowConstraints minHeight=&quot;10.0&quot; prefHeight=&quot;30.0&quot; vgrow=&quot;SOMETIMES&quot; \/&gt;         &lt;\/rowConstraints&gt;          &lt;children&gt;             &lt;Label alignment=&quot;CENTER&quot; maxHeight=&quot;1.7976931348623157E308&quot; maxWidth=&quot;1.7976931348623157E308&quot; onMouseClicked=&quot;#clear&quot; text=&quot;\u0423\u0414\u0410\u041b.&quot; \/&gt;             &lt;Label fx:id=&quot;divide&quot; alignment=&quot;CENTER&quot; maxHeight=&quot;1.7976931348623157E308&quot; maxWidth=&quot;1.7976931348623157E308&quot; onMouseClicked=&quot;#handleOperationSelect&quot; text=&quot;\u00f7&quot; GridPane.rowIndex=&quot;1&quot; \/&gt;             &lt;Label fx:id=&quot;multiply&quot; alignment=&quot;CENTER&quot; maxHeight=&quot;1.7976931348623157E308&quot; maxWidth=&quot;1.7976931348623157E308&quot; onMouseClicked=&quot;#handleOperationSelect&quot; text=&quot;\u00d7&quot; GridPane.rowIndex=&quot;2&quot; \/&gt;             &lt;Label fx:id=&quot;subtract&quot; alignment=&quot;CENTER&quot; maxHeight=&quot;1.7976931348623157E308&quot; maxWidth=&quot;1.7976931348623157E308&quot; onMouseClicked=&quot;#handleOperationSelect&quot; text=&quot;\u2212&quot; GridPane.rowIndex=&quot;3&quot; \/&gt;             &lt;Label fx:id=&quot;add&quot; alignment=&quot;CENTER&quot; maxHeight=&quot;1.7976931348623157E308&quot; maxWidth=&quot;1.7976931348623157E308&quot; onMouseClicked=&quot;#handleOperationSelect&quot; text=&quot;+&quot; GridPane.rowIndex=&quot;4&quot; \/&gt;          &lt;\/children&gt;       &lt;\/GridPane&gt;    &lt;\/children&gt;    &lt;stylesheets&gt;       &lt;URL value=&quot;@..\/..\/..\/style\/base.css&quot; \/&gt;       &lt;URL value=&quot;@..\/..\/..\/style\/skin.css&quot; \/&gt;       &lt;URL value=&quot;@Calculator.css&quot; \/&gt;    &lt;\/stylesheets&gt; &lt;\/fx:root&gt; <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u0423 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0435 fx:id, \u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u043c\u044b \u0438 \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f. \u0414\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c enumeration \u0441 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f\u043c\u0438:  <\/p>\n<pre><code class=\"java\">public enum Operation {     ADD,     SUBTRACT,     MULTIPLY,     DIVIDE,     EQ; } <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u0432\u043e\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e org.hamcrest.Matcher. \u0411\u0443\u0434\u0435\u043c \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u043d\u0430\u0448\u0443 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e \u0432 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440, \u0430 \u0437\u0430\u0442\u0435\u043c, \u043f\u0440\u0438\u0432\u043e\u0434\u044f \u0432 \u043d\u0438\u0436\u043d\u0438\u0439 \u0440\u0435\u0433\u0438\u0441\u0442\u0440, \u0431\u0443\u0434\u0435\u043c \u0441\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u0442\u044c \u0441 \u043f\u043e\u0441\u0442\u0443\u043f\u0430\u044e\u0449\u0438\u043c\u0438 \u043d\u0430 \u0432\u0445\u043e\u0434 \u043e\u0431\u044a\u0435\u043a\u0442\u0430\u043c\u0438.<\/p>\n<pre><code class=\"java\">public class OperationMatcher implements Matcher&lt;Node&gt; { \tprivate Operation operation;  \tpublic OperationMatcher(Operation operation) { \t\tthis.operation = operation; \t}  \t@Override \tpublic boolean matches(Object item) { \t\tif (item instanceof Labeled) { \t\t\tString expected = operation.toString().toLowerCase(); \t\t\tString id = ((Labeled)item).getId(); \t\t\tif (id != null) { \t\t\t\tif (expected.equals(id.toLowerCase())) { \t\t\t\t\treturn true; \t\t\t\t} \t\t\t} \t\t\t \t\t} \t\treturn false; \t} ... <\/code><\/pre>\n<p>  \u041a\u043e\u043d\u0435\u0447\u043d\u043e, \u0442\u0443\u0442 \u043c\u043d\u043e\u0433\u043e \u043b\u0438\u0448\u043d\u0435\u0433\u043e \u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043b, \u043d\u043e \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c, \u0447\u0442\u043e item \u2014 \u044d\u0442\u043e \u0432 \u043f\u0435\u0440\u0432\u0443\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c node \u0438 \u043a \u043d\u0435\u043c\u0443 \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u043c\u044b \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0438 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u0438\u044f. \u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043c\u0435\u0442\u043e\u0434\u043e\u043c GuiTest: <br \/>  public GuiTest click( Matcher matcher, MouseButton\u2026 buttons ), \u0430 \u0438\u043c\u0435\u043d\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043c\u0435\u0442\u043e\u0434:<\/p>\n<pre><code class=\"java\">private void perform(Operation operation) { \tMatcher&lt;Node&gt; matcher = new OperationMatcher(operation); \tcontroller.click(matcher, MouseButton.PRIMARY); } <\/code><\/pre>\n<p>  \u0418\u0442\u0430\u043a, \u043d\u0430\u043c \u043e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u043f\u043e\u043b\u0443\u0447\u0430\u044e\u0449\u0438\u0439\u0441\u044f \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442. \u0422\u043e \u0435\u0441\u0442\u044c \u043d\u0430\u0439\u0442\u0438 label (operation) \u0438 textField (input)\u2026 \u041d\u0438\u043a\u0442\u043e \u043d\u0435 \u0437\u0430\u043f\u0440\u0435\u0449\u0430\u0435\u0442 \u043d\u0430\u043c \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0435\u0449\u0451 matcher-\u043e\u0432 \u2014 \u0443 GuiTest \u0435\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0435\u0441\u0442\u044c \u043c\u0435\u0442\u043e\u0434 \u043f\u043e\u0438\u0441\u043a\u0430 \u043f\u043e matcher-\u0443.<\/p>\n<p>  \u041e\u0434\u043d\u0430\u043a\u043e \u044f \u043f\u043e\u043a\u0430\u0436\u0443 \u0434\u0440\u0443\u0433\u043e\u0439 \u0441\u043f\u043e\u0441\u043e\u0431, \u0430 \u0438\u043c\u0435\u043d\u043d\u043e \u043f\u043e\u0438\u0441\u043a \u043f\u043e styleClass (sleep \u0432\u0441\u0442\u0430\u0432\u0438\u043b \u043e\u043f\u044f\u0442\u044c \u0436\u0435 \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u044b \u2014 \u043d\u0430\u0434\u043e \u0434\u043e\u0436\u0434\u0430\u0442\u044c\u0441\u044f \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u043a\u0438):<\/p>\n<pre><code class=\"java\">public void checkDescriptionField(String expectedText)\tthrows InterruptedException { \tThread.sleep(200); \tNode result = controller.find(&quot;.operation&quot;); \tString actualText = ((Labeled) result).getText(); \tAssert.assertEquals(expectedText.trim(), actualText.trim()); }  public void checkInputField(String expectedText) throws InterruptedException { \tThread.sleep(200); \tNode result = controller.find(&quot;.input&quot;); \tString actualText = ((TextField) result).getText(); \tAssert.assertEquals(expectedText.trim(), actualText.trim()); } <\/code><\/pre>\n<p>  \u041f\u0440\u0438\u0448\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u0434\u043b\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0441\u0442\u0435\u0439\u0448\u0438\u0445 \u0442\u0435\u0441\u0442\u043e\u0432 \u043d\u0430 \u0441\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438 \u0432\u044b\u0447\u0438\u0442\u0430\u043d\u0438\u0435:<\/p>\n<pre><code class=\"java\">@Test public void testADD() throws InterruptedException { \tint digit1 = random.nextInt(1000); \tint digit2 = random.nextInt(1000);  \tclick(digit1); \tcheckDescriptionField(String.valueOf(digit1)); \tcheckInputField(String.valueOf(digit1));  \tperform(Operation.ADD);  \tclick(digit2); \tcheckDescriptionField(digit1 + &quot; + &quot; + digit2); \tcheckInputField(String.valueOf(digit2));  \tperform(Operation.EQ);  \tcheckInputField(String.valueOf(digit1 + digit2) + &quot;,00&quot;); } \t @Test public void testSubstract() throws InterruptedException { \tint digit1 = random.nextInt(1000); \tint digit2 = random.nextInt(1000);  \tclick(digit1); \tcheckDescriptionField(String.valueOf(digit1)); \tcheckInputField(String.valueOf(digit1));  \tperform(Operation.SUBTRACT);  \tclick(digit2); \tcheckDescriptionField(digit1 + &quot; \u2212 &quot; + digit2); \tcheckInputField(String.valueOf(digit2));  \tperform(Operation.EQ);  \tcheckInputField(String.valueOf(digit1 - digit2) + &quot;,00&quot;); } <\/code><\/pre>\n<p>  &quot;,00&quot; \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u044b \u2014 \u043f\u043e\u043d\u044f\u0442\u043d\u043e, \u0447\u0442\u043e \u043d\u0430\u0434\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u0447\u0435\u0440\u0435\u0437 Formatter-\u044b, \u043f\u043e\u043d\u044f\u0442\u043d\u043e, \u0447\u0442\u043e \u043d\u0430\u0434\u043e \u0437\u0430\u043c\u0435\u043d\u044f\u0442\u044c Thread.sleep \u043d\u0430 \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u0435, \u0430 \u043a\u043b\u0438\u043a\u0438 \u043d\u0430 \u043f\u0440\u043e\u043a\u0438\u0434\u044b\u0432\u0430\u043d\u0438\u0435 event-\u043e\u0432 \u2014 \u0442\u043e\u0433\u0434\u0430 \u0442\u0435\u0441\u0442\u044b \u043d\u0430\u0447\u043d\u0443\u0442 \u043b\u0435\u0442\u0430\u0442\u044c. \u041d\u043e \u044d\u0442\u043e \u0443\u0436\u0435 \u0432\u044b\u0445\u043e\u0434\u0438\u0442 \u0437\u0430 \u0440\u0430\u043c\u043a\u0438 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430 \u043f\u0440\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 TestFX. <\/p>\n<p>  \u041a\u0441\u0442\u0430\u0442\u0438, \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u043b \u0432\u0430\u043c \u043f\u0440\u043e TestFX \u0442\u0440\u0435\u0442\u044c\u0435\u0439 \u0432\u0435\u0440\u0441\u0438\u0438, \u2014 \u0431\u0443\u043a\u0432\u0430\u043b\u044c\u043d\u043e \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043d\u0435\u0434\u0435\u043b\u044c \u043d\u0430\u0437\u0430\u0434 \u0432\u044b\u0448\u043b\u0430 <a href=\"https:\/\/bintray.com\/testfx\/testfx\">alpha \u0432\u0435\u0440\u0441\u0438\u044f 4.0.1<\/a>. \u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u0430 \u0447\u0430\u0441\u0442\u044c testfx-legacy, \u043d\u043e \u043e\u0431 \u044d\u0442\u043e\u043c \u044f \u043d\u0430\u043f\u0438\u0448\u0443, \u043a\u043e\u0433\u0434\u0430 \u043f\u043e\u0433\u0440\u0443\u0436\u0443\u0441\u044c \u0433\u043b\u0443\u0431\u0436\u0435 \u0432 \u0438\u0441\u0445\u043e\u0434\u043d\u0438\u043a\u0438, &#8212;<a href=\"http:\/\/qabok.com\/bok-qa\/\"> \u0441\u0442\u0430\u0442\u044c\u044e \u043e\u043f\u0443\u0431\u043b\u0438\u043a\u0443\u044e \u0442\u0443\u0442 <\/a>\u043d\u0430 \u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u043e\u043c.<\/p>\n<p>  \u041e\u0431\u0435\u0449\u0430\u043d\u043d\u043e\u0435 \u0432\u0438\u0434\u0435\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u043e\u0432 \u043d\u0438\u0436\u0435:<\/p>\n<p>  <iframe loading=\"lazy\" width=\"560\" height=\"349\" src=\"\/\/www.youtube.com\/embed\/Jfq9KVpkuu4?wmode=opaque\" frameborder=\"0\" allowfullscreen><\/iframe>     \t<\/p>\n<div class=\"clear\"><\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"http:\/\/habrahabr.ru\/post\/257981\/\"> http:\/\/habrahabr.ru\/post\/257981\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>     \t<img decoding=\"async\" src=\"\/\/habrastorage.org\/files\/864\/62a\/07a\/86462a07a43947d49b2226fd453807c1.jpg\"\/><\/p>\n<p>  \u0414\u043e\u0431\u0440\u044b\u0439 \u0434\u0435\u043d\u044c!<\/p>\n<p>  \u0412 \u043c\u0438\u0440\u0435, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0438 \u043d\u0430 \u044d\u0442\u0430\u043f\u0435 \u0432\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u044f \u043f\u0440\u0435\u0432\u044b\u0448\u0430\u0435\u0442 \u0432 \u0441\u043e\u0442\u043d\u0438 \u0438 \u0442\u044b\u0441\u044f\u0447\u0438 \u0440\u0430\u0437 \u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u044c \u0438\u0441\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043d\u0430 \u044d\u0442\u0430\u043f\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438, \u043d\u0443\u0436\u043d\u043e \u0432\u0441\u0435\u0433\u0434\u0430 \u0438\u0441\u043a\u0430\u0442\u044c \u043e\u0442\u0432\u0435\u0442 \u043d\u0430 \u0432\u043e\u043f\u0440\u043e\u0441: \u00ab\u0430 \u043a\u0430\u043a \u044d\u0442\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438?\u00bb \u0412\u043e\u043f\u0440\u043e\u0441\u044b \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f JavaFX \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u0430\u044f \u043f\u0430\u0443\u0442\u0438\u043d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0435 \u043e\u0441\u0432\u0435\u0449\u0430\u0435\u0442. \u041d\u043e \u0432\u0441\u0451 \u0436\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043d\u0430\u0439\u0442\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0445 \u0438\u0434\u0435\u0439, \u0438 \u044f \u0445\u043e\u0447\u0443 \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u0441 \u0432\u0430\u043c\u0438 \u0441\u0432\u043e\u0438\u043c\u0438 \u043d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u044f\u043c\u0438.<\/p>\n<p>  \u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u043a\u0430\u043a \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u044c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u043d\u0430 JavaFX \u0444\u043e\u0440\u043c\u0435, \u043a\u0430\u043a \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0438\u0445 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430, \u043a\u0430\u043a \u043a\u043b\u0438\u043a\u0430\u0442\u044c \u043d\u0430 \u043d\u0438\u0445 \u0438 \u0442\u0430\u043a \u0434\u0430\u043b\u0435\u0435. \u042d\u0442\u043e \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0439 \u043d\u0430\u0431\u043e\u0440 \u0432\u0445\u043e\u0434\u043d\u044b\u0445 \u0442\u043e\u0447\u0435\u043a \u0432 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f JavaFX \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-257375","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/257375","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=257375"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/257375\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=257375"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=257375"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=257375"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}