I'm working on a test automation framework which use TestNG. I decided to use Dependency Injection pattern in order to implement more readable, reusable page objects and tests.
I've chosen Google Guice due to TestNG provides built-in support to inject test objects with Guice Modules. I only had to specify my Guice Modules as you can see at next code snippet:
#Guice(modules = CommModule.class)
public class CommunicationTest {
#Inject
private Communication comms;
#Test
public void testSendMessage() {
Assertions.assertThat(comms.sendMessage("Hello World!")).isTrue();
}
}
So far so good, although I'm going to need more advance DI features such as:
Lifecycle management
Configuration to field mapping
Generic binding annotations
Therefore, I'd like to use Netflix/Governator since it enhance Google Guice with these features. In order to trigger Governator features I must create the Injector through it instead of TestNG. e.g:
Injector injector = LifecycleInjector.builder()
.withModules(CommModules.class).build().createInjector();
And I'd like to do it mostly transparent as possible like TestNG does it.
I would like to know if:
Is it possible to provide my own Injector instance to TestNG in order to reuse #Guice annotation approach ?
Do you know any library for integrating Governator with TestNG ?
You can find in here what I've done so far.
This was not possible until now. I have fixed this in the latest snapshot version of TestNG. It should be available in the upcoming version of TestNG (Any version greater than 7.0.0)
The issue that I created to track this : https://github.com/cbeust/testng/issues/2199
In a nutshell, you can do the following :
Implement the interface org.testng.IInjectorFactory
Plugin the fully qualified class name of the newly created implementation via the command line argument -dependencyinjectorfactory
Since Allow user to provide DI Injector TestNG feature is going to be present in versions greater than 7.0.0. I implemented a solution using TestNG version 7.0.0 listeners.
Firstly, I created a module called autopilot-testng-runner with the following dependencies:
<dependencies>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
</dependency>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
</dependency>
<dependency>
<groupId>com.netflix.governator</groupId>
<artifactId>governator</artifactId>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
</dependency>
</dependencies>
This module contains the artifacts described at next:
#AutopilotTest: Custom annotation for declaring which Guice modules must be used for creating the Injector with LifecycleInjector.builder(). I couldn't reuse #Guice annotation due to TestNG also will create its Injector and declared dependencies will be created twice.
AutopilotSuiteListener: Implementation of ISuiteListener for creating the parent Injector, Governator's LifecycleManager instance and bind configuration properties before Suite starts. Therefore each Suite is going to have a parent Injector built with Governator and a life-cycle manager.
AutopilotTestListener: ITestListener implementation in charge of injecting dependencies in the running test case.
META-INF/services/org.testng.ITestNGListener: Service provider configuration file containing the fully qualified names of both ITestNGListener implementations.
Then, I added autopilot-testng-runner as a maven dependency in my project
<dependency>
<groupId>com.github.eljaiek.playground</groupId>
<artifactId>autopilot-testng-runner</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
And finally, I replaced #Guice annotation with #AutopilotTest
#AutopilotTest(modules = CommModule.class)
public class CommunicationTest {
#Inject
private Communication comms;
#Test
public void testSendMessage() {
Assertions.assertThat(comms.sendMessage("Hello World!")).isTrue();
}
}
Related
I have been into this for a while. The related questions in StackOverflow are about the absence of the hamcrest-core JAR in the classpath of the project, and the solutions are all related to its addition. What I am trying to do is de opposite: removing this dependency from the classpath.
Consider a Maven project with a single test case:
import static org.junit.Assert.assertTrue;
import org.junit.Test;
/**
* Unit test for simple App.
*/
public class AppTest {
/**
* Rigorous Test :-)
*/
#Test
public void shouldAnswerWithTrue() {
assertTrue(true);
}
}
Here the test shouldAnswerWithTrue invokes the method assertTrue in the class Assert from the dependency junit version 4.11 (declared in the POM). When I construct the corresponding call graph, the dependency hamcrest-core seems not to be used in this test case. hamcrest-core is a transitive dependency induced by the direct dependency junit. Therefore, I proceed to exclude it from the POM of my project as follows:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
</exclusion>
</exclusions>
</dependency>
However, when I execute mvn package, it triggers the following error:
java.lang.NoClassDefFoundError: org/hamcrest/SelfDescribing
I do not understand why Java is complaining about the interface SelfDescribing in a dependency that is not used at all in my test and in any of its methods calls. I have checked that no class from hamcrest-core is loaded from the Assert classes in JUnit.
So, Why I cannot exclude hamcrest-core? Why is this interface needed? Where is it called?
Because JUnit 4.11 actually depends on it at compile time: it uses it in its exception hierarchy. When the AssumptionViolatedException class is loaded, it will trigger a load of SelfDescribing.
So I have this springboot application which I'm migrating from a WAS to a springboot setup. And I have a couple of JSPs which has to be configured. To accomodate these I added the following dependencies:
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>9.0.22</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<scope>provided</scope>
</dependency>
The application already came with the following dependency which is being used throughout the application:
<dependency>
<groupId>com.ibm</groupId>
<artifactId>com.ibm-jaxrpc-client</artifactId>
<version>6.0</version>
</dependency>
The issue I'm facing is that both these dependencies (jaxrpc-client and tomcat-embed-jasper) have javax.servlet.ServletContext classes in them which is causing the following error:
The method's class, javax.servlet.ServletContext, is available from the following locations:
jar:file:/C:/Users/.m2/repository/com/ibm/com.ibm-jaxrpc-client/6.0/com.ibm-jaxrpc-client-6.0.jar!/javax/servlet/ServletContext.class
jar:file:/C:/Users/.m2/repository/org/apache/tomcat/embed/tomcat-embed-core/9.0.30/tomcat-embed-core-9.0.30.jar!/javax/servlet/ServletContext.class
It was loaded from the following location:
file:/C:/Users/.m2/repository/com/ibm/com.ibm-jaxrpc-client/6.0/com.ibm-jaxrpc-client-6.0.jar
Action:
Correct the classpath of your application so that it contains a single, compatible version of javax.servlet.ServletContext
I can't afford to remove any of these dependencies. jaxrpc-client is being referenced in the code already in too many places and I need tomcat-embed-jasper to render my jsp pages. I can't exclude the ServletContext class since its not a dependency(If I'm not wrong about the concept of exclusion). Please help with with a way forward with this issue.
I'm not familiar with IBM's jaxrpc client, but I assume, you have this exception in runtime, when trying to load the application.
In this case consider the following approaches:
Use another jax-rpc client library
Consider Loading the code that uses this library with the different class-loader (you'll have to create one classloader for this) to avoid the clash
Kind of paraphrasing the second option. You can "play" (override the order of loading of specific classes) with spring boot classloader as described in this article
I know, this is too general answer, but hopefully its still helpful.
The first solution is by far the easiest way I can think of.
The second solution is doable, however it pretty much depends on how exactly the code that uses the jax rpc client is loaded and used.
This question is about the proper way to read configuration in a REST service in a portable way, e.g. should run on Thorntail 2.4.0 and Wildfly 15.
This was the original implementation suggested by Thorntail
#Inject
#org.wildfly.swarm.spi.runtime.annotations.ConfigurationValue("swarm.port.offset")
private Optional<String> portOffset;
This was not working in WildFly 15 so we changed this code in the following way:
#Inject
#ConfigProperty(name="swarm.port.offset")
private Optional<String> portOffset;
And provided the system property is set, it works nicely.
However, back in Thorntail, it generates the following exception:
org.jboss.weld.exceptions.DeploymentException:
WELD-001408:
Unsatisfied dependencies for type Optional with qualifiers
#ConfigProperty
at injection point [BackedAnnotatedField] #Inject
#ConfigProperty
private com.my-company.core.internal.util.ZookeeperRegistry.portOffset
at com.my-company.core.internal.util.ZookeeperRegistry.portOffset(ZookeeperRegistry.java:0)
WELD-001475: The following beans match by type, but none have matching
qualifiers:
- Producer Method [Optional] with qualifiers [#Any #ConfigurationValue] declared as [[UnbackedAnnotatedMethod]
#ConfigurationValue #Dependent #Produces
org.wildfly.swarm.container.runtime.cdi.ConfigurationValueProducer.produceOptionalConfigValue(InjectionPoint)]
Many thanks in advance.
The code runs finally on both environments, with a single pom file.
I detail below the solution adopted.
Used #ConfigProperty, not #org.wildfly.swarm.spi.runtime.annotations.ConfigurationValue
Use #Any #ConfigProperty, resolved WELD-001475
In terms of maven dependencies, I included this dependency regardless of whether we are building for Thorntail or for WildFLy
<dependency>
<groupId>org.eclipse.microprofile.config</groupId>
<artifactId>microprofile-config-api</artifactId>
</dependency>
The actual version is resolved using dependencyManagement for the Eclipse microprofile:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.eclipse.microprofile</groupId>
<artifactId>microprofile</artifactId>
<version>2.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
...
</dependencyManagement>
Maven profiles are used to import Thorntail implementations that are not "core", e.g. microprofile-health, but in the case of microprofile-config, it is not necessary. For WildFly, the implementation org.wildfly.extension.microprofile.config.smallrye is provided so the library shall not be included in the war/ear.
For mybatis-spring-boot-starter-test I see no main method and even no Java file. It has two dependencies of which mybatis-spring-boot-test-autoconfigure contains some test files and I can execute them while spring-boot-starter-test just has a pom file.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-test-autoconfigure</artifactId>
</dependency>
So how can I execute these test modules? It I can't, it's created for what?
This module is not for "execution". Take a look at their docs:
What is MyBatis-Spring-Boot-Starter-Test?
The MyBatis-Spring-Boot-Starter-Test help creating a test cases for MyBatis component using the MyBatis-Spring-Boot-Starter.
By using this module you will can be:
Can use the #MybatisTest that setup test components for testing pure MyBatis component
Can import dependency artifacts for performing tests for pure MyBatis component
So, this module provides you #MybatisTest from mybatis-spring-boot-test-autoconfigure. That's, basically, what "starters" are: a group of (possibly one, like in this case) dependencies that are intended to work together to provide some features.
Read more about using #MybatisTest.
I'm a newbie to Unit testing Rest Webservices.
In our project, I'm trying to write a simple UT for Rest Services using Jersey test framework. I have upgraded our Jersey(1.18.1) to latest version(2.3.15) by replacing the following jars and using latest versions of Jersey Test Frameworks.
I'm able to handle almost all the dependencies except this one. When I try to run the test case, I'm getting the following exception :
Could anybody suggest me what I am missing.
Thanks!
I highly suggest using Maven. All you need to add is this
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>2.3.1</version>
</dependency>
And it will pull in all the required jars
public class SimpleJerseyTest extends JerseyTest {
#Path("/hello")
public static class HelloResource {
#GET
public String getHello() {
return "Hello World";
}
}
#Override
public Application configure() {
return new ResourceConfig(HelloResource.class);
}
#Test
public void test() {
String response = target("hello").request().get(String.class);
Assert.assertEquals("Hello World", response);
}
}
By the way, 2.3.1 is not the latest version, Jersey just released 2.16. And the dependencies will be a little different from what you see above.
Some resources:
Jersey Documentation
Jersey Test Framework
Some examples from the source code tests
The above is just for basic support. If you need JSON support, you will need more jars. Or with Maven
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>${jersey.version}</version>
</dependency>
For multi-part support, you will also need more jars. With Maven
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
<version>${jersey.version}</version>
</dependency>
Both these depedencies require more than just one jar. Here is an image of all the jars for the above three Maven dependencies combined, if you need to manually acquire them. Though I strongly suggest using/learning Maven. It will make life so much easier.