Давайте предположим, что мы должны выполнить кучу приемочных тестов с BDD-инфраструктурой, такой как Cucumber, как часть сборки Maven .
Использование Maven Failsafe Plugin не сложно. Но у него есть неявное требование : контейнер, в котором находится реализация, которую мы собираемся протестировать, должен быть уже запущен .
Многие контейнеры, такие как Jetty или JBoss, предоставляют свои собственные плагины Maven , чтобы позволить запускать сервер как часть работы Maven. И есть также хороший общий плагин Maven Cargo, который предлагает реализацию того же поведения для многих различных контейнеров.
Эти плагины позволяют, например, запускать сервер в начале задания Maven, развертывать реализацию, которую вы хотите протестировать, запускать тесты и останавливать сервер в конце.
Все механизмы, которые я описал, работают, и они обычно очень полезны для различных подходов к тестированию.
К сожалению, я не могу применить это решение, если мой контейнер не является поддерживаемым контейнером. Если не очевидно, я решил написать собственный плагин или добавить поддержку для моего конкретного контейнера в Maven Cargo.
В моем конкретном случае мне нужно было найти способ использовать JBoss Fuse от Red Hat, контейнер на базе Karaf .
Я решил постараться сохранить его легким и не писать полнофункциональный плагин Maven и в конечном итоге положиться на плагин GMaven , или как я недавно прочитал в интернете «Gradle для бедных» .
 GMaven — это в основном плагин для добавления поддержки Groovy к вашей работе в Maven, позволяющий вам выполнять фрагменты Groovy как часть вашей работы .  Мне это нравится, потому что это позволяет мне pom.xml скрипты прямо в pom.xml . 
Он также позволяет вам определять ваш скрипт в отдельном файле и выполнять его, но это то же самое поведение, которого вы могли бы достичь с помощью простого Java и плагина Maven Exec ; решение, которое мне не очень нравится, потому что скрывает реализацию и усложняет представление о том, чего пытается достичь полная сборка.
Очевидно, что этот подход имеет смысл, если сценарий, который вы собираетесь написать, достаточно прост, чтобы быть автоописательным.
Я опишу свое решение, начав с того, что поделюсь с вами своими пробами и ошибками и ссылками на различные статьи и посты, которые я нашел:
Сначала я решил использовать Maven Exec Plugin для непосредственного запуска моего контейнера. Что-то вроде того, что было предложено здесь http://stackoverflow.com/questions/3491937/i-want-to-execute-shell-commands-from-mavens-pom-xml
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
13 
14 
15 
16 
17 
 | 
<plugin>   <groupid>org.codehaus.mojo</groupid>   <artifactid>exec-maven-plugin</artifactid>   <version>1.1.1</version>   <executions>     <execution>       <id>some-execution</id>       <phase>compile</phase>       <goals>         <goal>exec</goal>       </goals>     </execution>   </executions>   <configuration>     <executable>hostname</executable>   </configuration> </plugin> | 
Этот вызов плагина, как часть задания Maven, фактически позволяет мне запускать контейнер , но у него есть огромный недостаток: жизненный цикл Maven останавливается до тех пор, пока внешний процесс не завершится или не будет остановлен вручную.
Это связано с тем, что выполнение внешнего процесса является «синхронным», а Maven не считает выполнение команды завершенным, поэтому он никогда не выполняется с остальными инструкциями по сборке.
Это не то, что мне было нужно, поэтому я искал что-то другое.
Сначала я нашел это предложение, чтобы запустить фоновый процесс, чтобы позволить Maven не блокировать: http://mojo.10943.n7.nabble.com/exec-maven-plugin-How-to-start-a-background-process -с-Exec-Exec-td36097.html
Идея здесь состоит в том, чтобы выполнить сценарий оболочки, который запускает фоновый процесс и немедленно возвращается .
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
 | 
<plugin>  <groupid>org.codehaus.mojo</groupid>  <artifactid>exec-maven-plugin</artifactid>  <version>1.2.1</version>  <executions>    <execution>      <id>start-server</id>      <phase>pre-integration-test</phase>      <goals>        <goal>exec</goal>      </goals>      <configuration>        <executable>src/test/scripts/run.sh</executable>        <arguments>          <argument>{server.home}/bin/server</argument>        </arguments>      </configuration>    </execution>  </executions></plugin> | 
и сценарий
| 
 1 
2 
3 
 | 
#! /bin/sh$* > /dev/null 2>&1 &exit 0 | 
Этот подход действительно работает . Моя сборка Maven не останавливается, и выполняются следующие шаги жизненного цикла. Но у меня сейчас новая проблема.
Мои следующие шаги немедленно выполняются.
У меня нет возможности вызвать продолжение только после того, как мой контейнер запущен и работает. Просматривая немного больше, я нашел эту хорошую статью: http://avianey.blogspot.co.uk/2012/12/maven-it-case-background-process.html Статья, очень хорошо написанная, кажется, описывает точно мою сценарий. Это также применимо к моему точному контексту, пытаясь создать вкус Карафа. Для запуска процесса в фоновом режиме используется другой подход — плагин Antrun Maven . Я попробовал и, к несчастью, нахожусь в той же ситуации, что и раньше . Интеграционные тесты выполняются сразу после запроса на запуск контейнера, но до того, как контейнер будет готов .
Убедившись, что я не могу найти никакого готового решения, я решил взломать текущее с помощью какого-то императивного кода.
Я подумал, что мог бы вставить «сценарий ожидания» после запуска запроса, но до запуска интеграционного теста, который мог бы проверить условие, которое гарантирует мне, что контейнер доступен.
Итак, если контейнер запущен на этом этапе:
| 
 1 
 | 
pre-integration-test | 
и мои приемочные испытания начались в течение ближайшего
| 
 1 
 | 
integration-test | 
  Я могу вставить некоторую логику в pre-integration-test который продолжает опрашивать мой контейнер и который возвращается только после того, как контейнер «считается» доступным. 
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
13 
14 
15 
16 
17 
 | 
import static com.jayway.restassured.RestAssured.*;println("Wait for FUSE to be available")for(int i = 0; i < 30; i++) {    try{        def status = response.getStatusLine()        println(status)        } catch(Exception e){            Thread.sleep(1000)            continue        }finally{            print(".")        }        if( !(status ==~ /.*OK.*/) )            Thread.sleep(1000)} | 
И выполняется этим экземпляром GMaven:
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
 | 
<plugin>    <groupid>org.codehaus.gmaven</groupid>    <artifactid>gmaven-plugin</artifactid>    <configuration>        <providerselection>1.8</providerselection>    </configuration>    <executions>        <execution>            <id>########### wait for FUSE to be available ############</id>            <phase>pre-integration-test</phase>            <goals>                <goal>execute</goal>            </goals>            <configuration>                <source><![CDATA[                            import static com.jayway.restassured.RestAssured.*;                            ...                            }                            ]]>            </configuration>        </execution>    </executions></plugin> | 
Мой (некрасивый) скрипт использует логику « Успокоение» и логику на основе исключений, чтобы в течение 30 секунд проверять, будет ли доступен веб-ресурс, который, как я знаю, развертывает мой контейнер.
Эта проверка не так надежна, как хотелось бы , поскольку она проверяет конкретный ресурс, но необязательно подтверждение того, что весь процесс развертывания завершен. В конце концов, лучшим решением было бы использование некоторого API-интерфейса управления, который мог бы проверять состояние контейнера, но, честно говоря, я не знаю, выставлены ли они Карафом, и моей простой проверки было достаточно для моего случая ограниченного использования.
С вызовом GMaven, теперь моя сборка maven ведет себя так, как я хотел.
В этом посте был показан способ обогатить ваш скрипт Maven некоторой программной логикой без необходимости написания полнофункционального плагина Maven. Поскольку у вас есть полный доступ к контексту Groovy, вы можете подумать, что можете выполнить любую задачу, которая может оказаться полезной. Например, вы также можете запустить фоновые потоки , которые позволят жизненному циклу Maven развиваться, продолжая выполнять вашу логику.
Мое последнее предложение — постараться сохранить логику в ваших скриптах простой и не превращать их в длинные и сложные программы. Читаемость была причиной, по которой я решил использовать уверенность вместо прямого доступа к Apache HttpClient .
  Это образец полного pom.xml 
| 
 001 
002 
003 
004 
005 
006 
007 
008 
009 
010 
011 
012 
013 
014 
015 
016 
017 
018 
019 
020 
021 
022 
023 
024 
025 
026 
027 
028 
029 
030 
031 
032 
033 
034 
035 
036 
037 
038 
039 
040 
041 
042 
043 
044 
045 
046 
047 
048 
049 
050 
051 
052 
053 
054 
055 
056 
057 
058 
059 
060 
061 
062 
063 
064 
065 
066 
067 
068 
069 
070 
071 
072 
073 
074 
075 
076 
077 
078 
079 
080 
081 
082 
083 
084 
085 
086 
087 
088 
089 
090 
091 
092 
093 
094 
095 
096 
097 
098 
099 
100 
101 
102 
103 
104 
105 
106 
107 
108 
109 
110 
111 
112 
113 
114 
115 
116 
117 
118 
119 
120 
121 
122 
123 
124 
125 
126 
127 
128 
129 
130 
131 
132 
133 
134 
135 
136 
137 
138 
139 
140 
141 
142 
143 
144 
145 
146 
147 
148 
149 
150 
151 
152 
153 
154 
155 
156 
157 
158 
159 
160 
161 
162 
163 
164 
165 
166 
167 
168 
169 
170 
171 
 | 
<!--======== Start FUSE ================================================================= --><project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelversion>4.0.0</modelversion>    <name>${groupId}.${artifactId}</name>    <parent>        <groupid>xxxxxxx</groupid>        <artifactid>esb</artifactid>        <version>1.0.0-SNAPSHOT</version>    </parent>    <artifactid>acceptance</artifactid>    <properties>        <fuse .home="">/data/software/RedHat/FUSE/fuse_full/jboss-fuse-6.0.0.redhat-024/bin/</fuse>    </properties>    <build>        <plugins>            <plugin>                <artifactid>maven-failsafe-plugin</artifactid>                <version>2.12.2</version>                <executions>                    <execution>                        <goals>                            <goal>integration-test</goal>                            <goal>verify</goal>                        </goals>                    </execution>                </executions>            </plugin>            <plugin>                <groupid>org.apache.maven.plugins</groupid>                <artifactid>maven-surefire-plugin</artifactid>                <configuration>                    <excludes>                        <exclude>**/*Test*.java</exclude>                    </excludes>                </configuration>                <executions>                    <execution>                        <id>integration-test</id>                        <goals>                            <goal>test</goal>                        </goals>                        <phase>integration-test</phase>                        <configuration>                            <excludes>                                <exclude>none</exclude>                            </excludes>                            <includes>                                <include>**/RunCucumberTests.java</include>                            </includes>                        </configuration>                    </execution>                </executions>            </plugin>            <plugin>                <artifactid>maven-antrun-plugin</artifactid>                <version>1.6</version>                <executions>                    <execution>                        <id>############## start-fuse ################</id>                        <phase>pre-integration-test</phase>                        <configuration>                            <target>                                <exec dir="${fuse.home}" executable="${fuse.home}/start" spawn="true">                    </exec>                            </target>                        </configuration>                        <goals>                            <goal>run</goal>                        </goals>                    </execution>                </executions>            </plugin>            <plugin>                <artifactid>maven-antrun-plugin</artifactid>                <version>1.6</version>                <executions>                    <execution>                        <id>############## stop-fuse ################</id>                        <phase>post-integration-test</phase>                        <configuration>                            <target>                                <exec dir="${fuse.home}" executable="${fuse.home}/stop" spawn="true">                    </exec>                            </target>                        </configuration>                        <goals>                            <goal>run</goal>                        </goals>                    </execution>                </executions>            </plugin>            <plugin>                <groupid>org.codehaus.gmaven</groupid>                <artifactid>gmaven-plugin</artifactid>                <configuration>                    <providerselection>1.8</providerselection>                </configuration>                <executions>                    <execution>                        <id>########### wait for FUSE to be available ############</id>                        <phase>pre-integration-test</phase>                        <goals>                            <goal>execute</goal>                        </goals>                        <configuration>                            <source><![CDATA[import static com.jayway.restassured.RestAssured.*;println("Wait for FUSE to be available")for(int i = 0; i < 30; i++) {    try{        def response = with().get("http://localhost:8383/hawtio")        def status = response.getStatusLine()        println(status)        } catch(Exception e){            Thread.sleep(1000)            continue        }finally{            print(".")        }        if( !(status ==~ /.*OK.*/) )            Thread.sleep(1000)  }]]>                        </configuration>                    </execution>                </executions>            </plugin>            <!-- -->        </plugins>    </build>    <dependencies>        <!-- -->        <dependency>            <groupid>info.cukes</groupid>            <artifactid>cucumber-java</artifactid>            <version>${cucumber.version}</version>            <scope>test</scope>        </dependency>        <dependency>            <groupid>info.cukes</groupid>            <artifactid>cucumber-picocontainer</artifactid>            <version>${cucumber.version}</version>            <scope>test</scope>        </dependency>        <dependency>            <groupid>info.cukes</groupid>            <artifactid>cucumber-junit</artifactid>            <version>${cucumber.version}</version>            <scope>test</scope>        </dependency>        <dependency>            <groupid>junit</groupid>            <artifactid>junit</artifactid>            <version>4.11</version>            <scope>test</scope>        </dependency>        <!-- groovy script dependencies -->        <dependency>            <groupid>org.apache.httpcomponents</groupid>            <artifactid>httpclient</artifactid>            <version>4.2.5</version>        </dependency>        <dependency>            <groupid>com.jayway.restassured</groupid>            <artifactid>rest-assured</artifactid>            <version>1.8.1</version>        </dependency>    </dependencies></project> |