-
Notifications
You must be signed in to change notification settings - Fork 586
FAT tests
- Overview of Functional Acceptance Tests (FAT)
- FAT Project Layout
- How to run an existing FAT
- Overview of how a FAT test runs
- Using the fattest.simplicity infrastructure
- Debugging a FAT
- FAT test modes
- Repeating FAT tests for different feature/JEE levels
- Running FAT tests with different java levels
- Skipping tests based on java level
- Error/Warning/FFDC scanning
- Java 2 Security testing
- Using Docker containers with FATs via Testcontainers
- Adding libraries to the FAT classpath
- Adding libraries to a specific location
- Test Features
The majority of OpenLiberty tests are Functional Acceptance Tests (FAT). A FAT test project ends in _fat
and executes tests with a running OpenLiberty image. This differs from Unit tests because Unit tests do not execute on a running OpenLiberty image.
The example FAT project is build.example_fat
and can be used as a minimalistic reference point for writing new FATs.
Note that TCK projects follow a slightly different layout. A TCK project ends in _fat_tck
and require mvn
to be installed when run locally. See here for more information on creating and running a TCK project.
- com.ibm.ws.my_fat/
- fat/src/ # Root folder for test client side
- com/ibm/ws/my/
- FATSuite.java # Entry point of the FAT. Lists one or more test classes via '@SuiteClasses'
- MyTest.java # Test class containing a group of tests. Server and app lifecycle is controlled here.
- test-applications/ # Root folder for all test applications
- myApp/ # Folder name indicates name of application (by convention)
- resources/ # Optional: Static resources such as web.xml are included here for 'myApp'
- src/ # Java code for 'myApp' goes here
- publish/ # Root folder for non-java resources
- files/ # Files needed by test client go here. Such as alternate server.xml configurations
- servers/ # Root folder for OpenLiberty servers
- myServer/ # Folder representing a server. This folder gets copied to ${wlp.install.dir} at runtime
- server.xml # Server configuration for 'myServer'
- build/libs/autoFVT/ # Root folder for a single execution of FAT output
- output/ # Root folder for server output
- results/ # Root folder for test client output
- junit.html/ # Contains JUnit-style test results which can be viewed in a web browser
- output.txt # Log file for test client (i.e. fat/src/ classes)
- bnd.bnd # Describes source folders and compile-time build dependencies
- build.gradle # Build file. Must include "apply from: '../cnf/gradle/scripts/fat.gradle'"
To run a FAT, you must have ant
on your $PATH
.
To test this, you can run the command which ant
. If ant is not on your path, you can download ant here and then add export PATH=$ANT_HOME/bin:$PATH
to your ~/.bashrc. After adding ant to your path, run ./gradlew --stop
to stop the Gradle daemon so that it can be started with the updated $PATH
.
./gradlew <fat_project_name>:buildandrun
For example:
./gradlew build.example_fat:buildandrun
The buildandrun
task will build and run the FAT. If you are not making changes to the FAT and just want to re-run the FAT without rebuilding it, you can use the runfat
task instead.
./gradlew <fat_project_name>:buildandrun -Dfat.test.class.name=<fully_qualified_java_test_class>
For example:
./gradlew build.example_fat:buildandrun -Dfat.test.class.name=com.ibm.ws.example.SimpleTest
You no longer have to fully qualify the test name and can specify multiple test classes with a comma separated list:
./gradlew build.example_fat:buildandrun -Dfat.test.class.name=SimpleTestA,SimpleTestB
You can also use '*' to wildcard match multiple test classes:
./gradlew build.example_fat:buildandrun -Dfat.test.class.name=SimpleTest*
./gradlew <fat_project_name>:buildandrun -Dfat.test.class.name=<fully_qualified_java_test_class> -Dfat.test.method.name=<java_method_name>
For example:
./gradlew build.example_fat:buildandrun -Dfat.test.class.name=com.ibm.ws.example.SimpleTest -Dfat.test.method.name=exampleTest
You can now specify multiple test classes with a comma separated list:
./gradlew build.example_fat:buildandrun -Dfat.test.class.name=SimpleTest -Dfat.test.method.name=exampleTest1,exampleTest2
You can also use '*' to wildcard match multiple tests:
./gradlew build.example_fat:buildandrun -Dfat.test.class.name=SimpleTest -Dfat.test.method.name=exampleTest*
When a FAT runs there are two or more JVMs in use:
- A test-client side JVM which manages server lifecycle, deploys apps, and drives individual test cases on the server side.
- An OpenLiberty server with zero or more test apps running on it.
Most FATs use a test client to drive HTTP GET requests on a test servlet within a test application. The steps for this style of FAT test is the following.
- The build packages everything needed to run the FAT into an autoFVT.zip archive.
- On an automated build, the autoFVT.zip for each FAT is built on the parent build machine. Then the autoFVT.zip archives are distributed evenly among about a dozen child build engines where they will be extracted and run. This allows us to pack about 36 hours of FAT tests into about 3 hours.
- The build unzips autoFVT.zip and starts a JUnit process with the class named
FATSuite
as the entry point. Each test class in the@SuiteClasses
will be run in sequence. - Test class runs
- Test class
@BeforeClass
runs. Typically applications are deployed to server and then server is started here. - Test client runs each test case in class in sequence. Typically each test case is a single HTTP GET. For example:
GET http://localhost:8010/myApp/MyTestServlet?testMethod=myTest
- Test application receives HTTP GET request via a Servlet. For example, inside
MyTestServlet.doGet()
thetestMethod
parameter is checked, and reflection is used to invoke a method indicated by the parameter value, soMyTestSevlet.myTest()
is invoked. - If the test case passes, the test servlet prints a well-known success message string to the HTTPServletResponse. If the test case fails, the test servlet does not print the success message, and instead dumps the stack trace of the failure to the HTTPServletResponse.
- Test client reads HTTPServletResponse. If response contains well-known success message, the test passes. If the success message is missing, fail the test case with the HTTPServletResponse content as the failure message.
- Test application receives HTTP GET request via a Servlet. For example, inside
- Test class
@AfterClass
runs. Typically server is stopped here. When server is stopped, the server logs are copied from${wlp.install.dir}
intobuild/libs/autoFVT/output/servers/<server_name>-<stop_timestamp>
- Test class
The FAT test infrastructure is housed in the fattest.simplicity
project. This contains all of the junit extensions and extra bells and whistles available to FAT projects.
To get most of the custom value-adds of the simplicity infrastructure, test classes must indicate @RunWith(FATRunner.class)
on the test class. For example:
@RunWith(FATRunner.class)
public class SimpleTest { /* ... */ }
The LibertyServer
class gives the test client a POJO representation of an OpenLiberty server. The easiest way to obtain an object instance is with the @Server("<server_name>")
annotation. With this object, you can do things like start and stop a server.
An example usage typically looks like:
@RunWith(FATRunner.class)
public class MyFATTest {
@Server("MyServer")
public static LibertyServer server;
@BeforeClass
public static void setup() throws Exception {
server.startServer();
}
@AfterClass
public static void tearDown() throws Exception {
server.stopServer();
}
}
Typically FATs build application artifacts at FAT runtime using an open source library called ShrinkWrap, and then export the artifacts to disk. The ShrinkWrap API is quite flexible and can build many different types of archives including: .zip .jar .war .ear .rar
The official ShrinkWrap javadoc can be found here: ShrinkWrap API 1.2.3 API
The ShrinkWrap API can get a little verbose, so we have a ShrinkHelper
class to make conventional operations more concise. A few useful ones are:
-
ShrinkHelper.defaultApp(LibertyServer s, String appName, String... packages)
Builds an artifact named<appName>.war
which includes resources fromtest-applications/<appName>/resources/
and the specified java packages. Also exports the .war to the indicated LibertyServer's${server.config.dir}/apps/
directory. -
ShrinkHelper.defaultDropinApp(LibertyServer s, String appName, String... packages)
The same thing asShrinkHelper#defaultApp
except it exports the .war to the${server.config.dir}/dropins/
, which means no server.xml configuration is needed for the application. -
ShrinkHelper.buildDefaultApp(String appName, String... packages)
The same thing asShrinkHelper#defaultApp
except the archive is not exported to disk anywhere. -
ShrinkHelper.exportAppToServer(LibertyServer s, Archive<?> a)
Exports the archive to disk in the${server.config.dir}/apps/
directory. The name of the exported file will bea.getName()
. -
ShrinkHelper.exportToServer(LibertyServer s, String path, Archive<?> a)
Same thing asShrinkHelper#exportAppToServer
except you can indicate apath
to export the archive at (relative to${server.config.dir}
of the indicated server). -
ShrinkHelper.addDirectory(Archive<?> a, String path)
Adds a directory (and all sub-dirs) to the indicated archive.
There is a test-only feature in Liberty called componentTest-1.0
which will provide some common utilities for FAT test apps.
-
If the
componentTest-1.0
feature is enabled in server.xml, then JUnit will be made available to test applications. This is useful for being able to do server-side assertions. -
If the
componentTest-1.0
feature is enabled in server.xml, theFATServlet
class is made available to test applications. This class contains boiler-plate HttpServelt code for invoking test methods.
Declaring test methods on the server side using @TestServlet
Without @TestServlet
With standard JUnit a "test stub" would be needed on the client side for each server-side test. For example:
// Client side
public class MyTest {
// get and start server in @BeforeClass
@Test
public void myTest() throws Exception {
// invoke GET http://loclahost:8010/myApp/MyTestServlet?testMethod=myTest and assert response is successful
FATServletClient.runTest(server, "/myApp/MyTestServlet", "myTest");
}
}
// Server side
@WebServlet("/MyTestServlet")
public class MyTestServlet extends FATServlet {
public void myTest() throws Exception {
// run some server-side test
}
}
This is inconvenient because the client side code is completely boiler plate and if the server side test moves or is renamed it requires a corresponding update on the client side class.
With @TestServlet
To eliminate this, we have the @TestServlet
annotation which will scan the referenced test servlet class for @Test
methods and add synthetic client-side test stubs at runtime. So the example above becomes:
// Client side
@RunWith(FATRunner.class)
public class MyTest {
@Server("MyServer")
@TestServlet(servlet = MyTestServlet.class, contextRoot = "myApp") // References class gets scanned for @Test methods
public static LibertyServer server;
// start server in @BeforeClass
}
// Server side
@WebServlet("/MyTestServlet")
public class MyTestServlet extends FATServlet {
@Test // Able to use standard JUnit @Test annotation in test servlet
public void myTest() throws Exception {
// run some server-side test
}
}
In a typical FAT there are two or more JVMs (client driver, and a server).
To debug the test client, use the -Ddebug.framework
option and attach a debugger on port 6666. For example:
./gradlew build.example_fat:buildandrun -Ddebug.framework
If you are using the Eclipse IDE, you can attach a debugger by selecting the debug configuration Liberty-FAT remote debug
from the debug menu dropdown. If you've never used this debug configuration, it will not appear in the dropdown until you've visited Debug Configurations... from the Debug dropdown. Look for 'Liberty-FAT remote debug' under 'Remote Java Application'.
To debug a liberty server, use the -Ddebug.server
option and attach a debugger on port 7777. For example:
./gradlew build.example_fat:buildandrun -Ddebug.server
If you are using the Eclipse IDE, you can attach the debugger by selecting Liberty- server remote debug
from the debug menu dropdown. See above if this item doesn't appear in your debug dropdown.
There are two primary FAT test modes: LITE
and FULL
, with LITE
being the default mode.
The LITE
mode is intended to be for "golden path" flows and the 80% use cases (within reason). These are the tests that go through:
- The most common or ideal scenario, where everything is configured exactly as it needs to be.
- The configurations or scenarios that are most likely to appear in the field, including error path cases.
Tests that are configured to run in LITE
mode will run every time the bucket is run. That means that the LITE
FAT tests are running multiple times a day across our various automated builds, which allows us to spot glaring regressions or errors in the product code more quickly.
The FULL
mode is intended for long-running tests, less common situations/configurations, edge cases, and generally anything that doesn't belong in the LITE
mode.
A FAT project should run in 5 minutes or less in LITE
mode, with each test case running in under 15 seconds. However, sometimes it is necessary to write tests that are important to run, but also time intensive.
The simplicity test framework has an @Mode
annotation for this purpose. If a test method does not define a @Mode
annotation, it will be considered a LITE
mode test. For example:
public class MyTest {
@Test
public void regularTest() { } // No @Mode annotation, so LITE mode is assumed
@Test
@Mode(LITE)
public void liteTest() { } // functionally equivalent to 'regularTest()'
@Test
@Mode(FULL)
public void testThatTakesAWhile() { } // Will only be run the FAT is run in FULL mode
}
To run a FAT in FULL
mode, specify -Dfat.test.mode=FULL
in the launch command, for example:
./gradlew build.example_fat:buildandrun -Dfat.test.mode=FULL
When we have multiple versions of a feature (e.g. cdi-1.2
and cdi-2.0
), it is useful to re-run the same tests on the newer feature levels (e.g. re-run the CDI 1.2 tests for both cdi-1.2
and cdi-2.0
) in order to prove that functionality that worked in previous feature versions continues to work in newer feature versions.
For example, if a FAT was written with Java EE 7 features, and you want to re-run the FAT with Java EE 8 equivalent features, you can do the following:
@RunWith(Suite.class)
@SuiteClasses({ ... })
public class FATSuite {
// Using the RepeatTests @ClassRule in FATSuite will cause all tests in the FAT to be run twice.
// First without any modifications, then again with all features in all server.xml's upgraded to their EE8 equivalents.
@ClassRule
public static RepeatTests r = RepeatTests.withoutModification()
.andWith(FeatureReplacementAction.EE8_FEATURES());
}
Note that when using the FeatureReplacementAction.EE8_FEATURES
action, the additional test iteration will be skipped automatically if the java level is less than Java 8.
For a more fine-grained approach, the RepeatTests
class rule can also be applied to individual test classes, for example:
@RunWith(FATRunner.class)
public class SomeTest extends FATServletClient {
// Runs all tests in 'SomeTest' first without modification, and then again after upgrading
// publish/servers/someServer/*.xml to use EE8 features
@ClassRule
public static RepeatTests r = RepeatTests.withoutModification()
.andWith(FeatureReplacementAction.EE8_FEATURES()
.forServers("someServer"));
You can have the repeated tests run in Full Fat only:
FeatureReplacementAction.EE8_FEATURES().fullFATOnly()
Or specify one of the FAT run modes (FULL, LITE, QUARANTINE, etc)
FeatureReplacementAction.EE8_FEATURES().withTestMode(TestMode.LITE)
Additionally, it is possible to exclude individual test methods or classes from a given repeat phase using the @SkipForRepeat
annotation. For example:
@Test
@SkipForRepeat(SkipForRepeat.NO_MODIFICATION)
public void testEE8Only() throws Exception {
// This test will skip for the EE7 feature (i.e. NO_MODIFICATION) iteration
}
@Test
@SkipForRepeat(SkipForRepeat.EE8_FEATURES)
public void testEE7Only() throws Exception {
// This test will skip for the EE8 feature iteration
}
And finally, for maximum level of flexibility, it is possible to write custom actions by implementing the RepeatTestAction
interface. For example, a FAT could be repeated with multiple database types in the following way:
public class RepeatTestsWithDB2 implements RepeatTestAction {
public RepeatTestsWithDB2(/* DB2 database info */) { }
@Override
public boolean isEnabled() { /* check if a DB2 database is available for use */ }
@Override
public void setup() { /* get and modify server.xml's to use DB2 database config */ }
}
And a custom action could be used as:
@ClassRule
public static RepeatTests r = RepeatTests.withoutModification()
.andWith(new RepeatTestsWithDB2(/* db2 database info */));
If helpful here is the componenttest.rules.repeater package source
Jakarta EE 9 brings the "big bang" javax.*
--> jakarta.*
package namespace change. We have a repeat action called componenttest.rules.repeater.JakartaEE9Action
in fattest.simplicity that will do 2 things:
- It will update all server.xml's in
autoFVT/publish/
to use their Jakarta EE 9 equivalent feature (e.g.servlet-4.0
-->servlet-5.0
) - Any archives that are exported via
ShrinkWrap
will be automatically transformed to Jakarta EE packages (e.g.javax.servlet
-->jakarta.servlet
)
To enable the Jakarta EE 9 repeat action in a specific test class or entire FAT bucket, add the following items:
- Add a
RepeatTests
@ClassRule
(if one is not present already) and append.andWith(new JakartaEE9Action())
. If theRepeatTest
rule is placed in theFATSuite.java
it will repeat the entire bucket. If it is placed on a specific test class, it will repeat the tests in that class.@ClassRule public static RepeatTests r = RepeatTests.withoutModification() // run all tests as-is (e.g. EE8 features) .andWith(new JakartaEE9Action()); // run all tests again with EE9 features+packages
- Add the following line in the FAT bucket's
build.gradle
file:addRequiredLibraries.dependsOn addJakartaTransformer
- Update the
bnd.bnd
file of the FAT bucket to include the new EE 9tested.features
. For example:tested.features: \ servlet-5.0,\ jsp-3.0
-
(Optional) If certain tests don't apply to Jakarta EE 9, they may be skipped by adding the
@SkipForRepeat(EE9_FEATURES)
annotation to the@Test
method or class element. -
(Optional) If any extra files/archives need to be transformed that aren't server.xml's or archives created by
ShrinkHelper
, you can programmatically invoke the JakartaEE transformer like this:if (JakartaEE9Action.isActive()) { Path someArchive = Paths.get(...); JakartaEE9Action.transform(someArchive); }
Normally everything in OpenLiberty runs/builds with JDK 8. If you want to build and run a FAT on a different java level locally, do the following:
export JAVA_HOME=/path/to/desired/jdk_home
export PATH=$JAVA_HOME/bin:$PATH
./gradlew --stop
./gradlew :com.ibm.ws.whatever_fat:buildandrun
NOTE: Stopping the gradle daemon (./gradlew --stop
) is important, because it will force a new gradle daemon to be created next time you run a gradle command which will contain the PATH
update.
Alternatively, if you only want the Liberty server to run with the controlled JDK (i.e. keep the build and the client side junit process on JDK 8), then instead do:
mkdir /path/to/open-liberty/dev/build.image/wlp/etc/
echo "JAVA_HOME=/path/to/desired/jdk_home" > /path/to/open-liberty/dev/build.image/wlp/etc/server.env
Currently OpenLiberty supports Java 7 and 8, with all new features requiring a minimum of Java 8 to run. As a result, any FATs for new features must not run on Java 7.
There are 3 ways to skip FAT tests based on Java level:
- Setting
javac.source: 1.8
in the bnd.bnd file (i.e. compiling the FAT with Java 8) will indicate that the entire FAT should skip on Java 7. - Setting
runtime.minimum.java.level: 1.8
in the bnd.bnd file will indicate that the entire FAT should skip on Java 7. - Using the
@MinimumJavaLevel(javaLevel = 8)
annotation on a test class or method will cause the annotated element to skip on Java 7.
Sometimes things can go wrong during a FAT test case that doesn't cause the JUnit test case to fail, such as an error or warning message in server logs. To catch this, whenever a LibertyServer is stopped, the messages.log file will be scanned for errors and warnings. If any errors or warnings are found, an exception is thrown which will trigger a class-level JUnit failure.
If a test generates errors or warnings that are expected to occur, then a list of expected errors can be passed into LibertyServer#stopServer. For example:
@AfterClass
public void tearDown() throws Exception {
// Some test generated the *expected* error: CWWK1234E: The server blew up because blah blah blah
server.stopServer("CWWK1234E"); // Stop the server and indicate the 'CWWK1234E' error message was expected
}
If a test generates an FFDC that is expected to occur, use the @ExpectedFFDC
annotation. For example:
@Test
@ExpectedFFDC("java.lang.IllegalStateException")
public void someErrorPathTest() {
// Do something that causes a java.lang.IllegalStateException
}
If a test generates an FFDC sometimes and we decide it's OK to allow, use the @AllowedFFDC
annotation. Obviously, this annotation should be used sparingly, and @ExpectedFFDC
should be used instead whenever possible. An example usage would be:
@Test
@AllowedFFDC // allows any FFDCs to occur
public void someNastyErrorPathTest() {
// Do something that could cause a variety of error paths to occur
}
@Test
@AllowedFFDC("java.lang.IllegalStateException")
public void someErrorPathTest() {
// Do something that *sometimes* causes a java.lang.IllegalStateException
}
NOTE: @AllowedFFDC()
can be added at the servlet level to apply to all tests in that class, or at the test runner class
to apply to all tests it runs.
OpenLiberty allows running with Java 2 Security (j2sec) enabled, and so the buildandrun
task runs with j2sec enabled (-Dglobal.debug.java2.sec=true
) by default to test this scenario.
This can be disabled by using -Dglobal.debug.java2.sec=false
, for example:
./gradlew build.example_fat:buildandrun -Dglobal.debug.java2.sec=false
Even if there are only a few distinct j2sec issues, the log files can quickly become overwhelming due to the volume of logs per issue multiplied by the potential frequency of issues. It also can be a slow, iterative process to fix the issues one at a time.
To speed up this process, the SecurityManager installed from -Dglobal.debug.java2.sec=true
will NOT blow up on j2sec issues, and will instead log a message.
After the FAT is done running, a report will be generated for each server instance in build/libs/autoFVT/ACEReport-<timestamp>.log
, and even more detailed information about the full list of AccessControlException(s) will be contained within each of these server's console logs.
The -D properties above cause the test framework to generate properties within each server's ${server.config.dir}/bootstrap.properties
file, and so the properties in this file can be edited directly by the experienced user (as in the example below). Note because of the test framework writing to these properties, simply editing the bootstrap.properties
in the test source will not necessarily help (since the framework will add its own values). See this Knowledge Center article for more info.
If a server should NOT run with java 2 security ever
Add this to the ${server.config.dir}/bootstrap.properties
file for the server you want to permanently disable java 2 security for:
websphere.java.security.exempt=true
Sometimes a FAT test requires an external resource, such as a database. In the past, external resources have been manually maintained on external machines, but this can result in a number of issues:
- The people who set up/maintained the external resource have moved on, and now nobody knows how to set up the resource
- The external resource crashes, and needs to be manually restarted
- Current tests and older service streams point to the same resources, but sometimes external resources need to be updated to newer versions as the FATs evolve
These issues are alleviated by putting external resources in Docker containers. Thanks to a library called Testcontainers, we can easily integrate our JUnit-based FATs with containers.
-
Add the following dependencies to your FAT's build.gradle file:
requiredLibs 'org.testcontainers:testcontainers:1.12.0', 'org.apache.commons:commons-compress:1.19', 'org.rnorth.duct-tape:duct-tape:1.0.7', 'org.rnorth.visible-assertions:visible-assertions:2.1.2', 'org.rnorth:tcp-unix-socket-proxy:1.0.2', 'net.java.dev.jna:jna:5.2.0', 'org.slf4j:slf4j-api:1.7.7', 'org.slf4j:slf4j-jdk14:1.7.7'
-
Add the following dependencies to your FAT's bnd.bnd file
-buildpath
:-buildpath: \ org.rnorth.duct-tape:duct-tape;version=1.0.7,\ org.testcontainers:testcontainers;version=1.12.0,\ org.slf4j:slf4j-api;version=1.7.7
-
Next, define the containers you want to start using the Testcontainers API, for example:
import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.output.OutputFrame; import com.ibm.websphere.simplicity.log.Log; // Can be added to the FATSuite to make the resource lifecycle bound to the entire // FAT bucket. Or, you can add this to any JUnit test class and the container will // be started just before the @BeforeClass and stopped after the @AfterClass @ClassRule public static GenericContainer<?> myContainer = new GenericContainer<>("foo:1.0") .withEnv("SOME_ENV_VAR", "some value") .withLogConsumer(MyTestClass::log); // This helper method is passed into `withLogConsumer()` of the container // It will consume all of the logs (System.out) of the container, which we will // use to pipe container output to our standard FAT output logs (output.txt) private static void log(OutputFrame frame) { String msg = frame.getUtf8String(); if (msg.endsWith("\n")) msg = msg.substring(0, msg.length() - 1); Log.info(c, "somecontainer", msg); }
By default, the FAT will run the containers on your local machine if you have Docker installed. If you do not have Docker installed, or if the FAT is running on a remote build engine, then an external pool of Docker host machines (IBM internal only) will be used. To force an external Docker host to be used, you can run the FAT with the -Dfat.test.use.remote.docker=true
option.
For a simple example of using Testcontainers in a FAT, check out the ValidateCloudantTest from the com.ibm.ws.rest.handler.validator.cloudant_fat bucket. A more complex example can be found in the com.ibm.ws.jdbc_fat_postgresql or com.ibm.ws.cloudant_fat buckets.
For more details on using Testcontainers in general, check out testcontainers.org
To add libraries to the FAT classpath - so that they are available from your test classes at runtime - add them under requiredLibs
in your dependencies in your build.gradle
file.
dependencies {
requiredLibs 'javax.ws.rs:javax.ws.rs-api:2.0',
'org.apache.cxf:cxf-core:3.1.16',
'org.apache.cxf:cxf-rt-frontend-jaxrs:3.1.16',
'org.apache.cxf:cxf-rt-rs-client:3.1.16',
'org.apache.cxf:cxf-rt-rs-extension-providers:3.1.16',
'org.apache.cxf:cxf-rt-transports-http:3.1.16'
}
Typically if your server needs a <library>
of some sort, you you will need to put a binary dependency at some specific location. This can be accomplished with the following code in the build.gradle
file.
configurations {
// Defines a dependency group called "myLibs"
// can have multiple dependency groups if needed
myLibs
}
dependencies {
// A dependency group can have multiple dependencies
myLibs 'com.foo.somelib:1.0',
'com.foo.anotherlib:2.0'
}
task copyMyLibs(type: Copy) {
shouldRunAfter jar
from configurations.myLibs
into new File(autoFvtDir, 'publish/shared/resources/myLibs')
// Optionally rename files as they are copied (useful for stripping off version numbers)
rename 'somelib-.*.jar', 'somelib.jar'
}
// Hook into the standard 'addRequiredLibraries' task that runs at FAT build time
addRequiredLibraries.dependsOn copyMyLibs
If your FAT is testing against a custom test feature, there is some project refactoring that will need to take place for your test feature to be available to the application during runtime.
- First create two new bnd files,
{my_fat}.bnd
and{my_feature}.bnd
in the root of your project.
- com.ibm.ws.my_fat/
...
- bnd.bnd # Describes source folders and compile-time build dependencies
- {my_fat}.bnd # Describes the main fat bundle
- {my_feature}.bnd # Describes the user feature fat bundle
- In the
bnd.bnd
file enable sub-features by including the line-sub: *.bnd
- In the
{my_fat}.bnd
file add the following properties to differentiate it from the test-feature.
-include= ~../cnf/resources/bnd/bundle.props
bVersion=1.0
#This file defines the main fat bundle
Bundle-Name: {Project_Name}
Bundle-SymbolicName: {Project_Name}
- In the
{my_feature}.bnd
file add the following properties and configurations
-include= ~../cnf/resources/bnd/bundle.props
bVersion=1.0
#This file defines the {my_feature}-1.0 user feature bundle
Bundle-Name: {my_feature}
Bundle-SymbolicName: {feature.package}; singleton:=true
Bundle-Description: Bundles that tests a user feature that uses persistent executor; version=${bVersion}
Export-Package: {feature.package};version=1.0
Private-Package: {feature.package}.internal*
Include-Resource:\
OSGI-INF/metatype/metatype.xml=test-bundles/userFeature/resources/OSGI-INF/metatype/metatype.xml
-dsannotations: \
{feature.package}.{component},\
{feature.package}.internal.{component}
- Add gradle dependency to build feature at runtime
dependencies {
requiredLibs project(':com.ibm.ws.my_fat')
}
task copyFeatureBundle(type: Copy) {
from buildDir
into new File(autoFvtDir, 'lib/LibertyFATTestFiles/bundles')
include '{feature.package}.jar'
}
autoFVT {
dependsOn copyFeatureBundle
}