I have this scenario:
Enterprise Application. Runs ok. --> contains MyFacade
Client application. Runs ok.
Ejb module. Runs ok, but can't test --> contains MyClass.java
DomainLibrary. --> contains entities and remote interfaces.
I want to run test file of point 3. (ejb module). Ok, first of all I put the Enterprise Application running. On second place I run the test file of the point 3.
The problem is that the remote interface ejb contained on Enterprise Application can't be found.
Error 1: It end up with an endless loop of the following output
WARNING: AS-CDI-005 Okt 22, 2013 4:49:23 PM org.glassfish.weld.BeanDeploymentArchiveImpl handleEntry
Error 1: Solved Running JUnit Tests on embedded glassfish 4 causing WARNING: AS-CDI-005
Error 2!!!:
javax.naming.NamingException: Lookup failed for [...] (MyFacade)
Ejb module: MyClass.java
#Singleton
public class MyClass implements MyClassLocal {
#EJB(lookup = "java:global/EnterpriseApplication-ejb/MyFacade!com.mydomain.repository.MyFacadeRemote")
private MyFacadeRemote myFacade;
public MyClass() {
}
public void bussinesMethod(){
System.out.println("Hello stackOverWorld! ");
myFacade.findAll();
}
}
Test method:
#Test
public void testBusinessMethod() throws Exception {
System.out.println("businessMethod");
Map<Object, Object> properties = new HashMap<Object, Object>();
properties.put(EJBContainer.APP_NAME, "MyEjbModule");
properties.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.enterprise.naming.SerialInitContextFactory");
EJBContainer container = EJBContainer.createEJBContainer(properties);
MyClassLocal instance = (MyClassLocal)container.getContext().lookup("java:global/MyEjbModule/classes/MyClass!com.mydomain.MyClassLocal");
//EJBContainer container = javax.ejb.embeddable.EJBContainer.createEJBContainer();
//MyClassLocal instance = (MyClassLocal)container.getContext().lookup("java:global/classes/MyClass");
instance.businessMethod();
container.close();
}
You may use the same approach we did for our EJB application. You may call some of the tests 'unit tests' but they are really integration tests. Running these tests includes starting the application server first, then running the JUnit test cases and finally stopping the server. These test cases act as the client, loading the ejb remote interface, make the appropriate calls, validate the return values, etc just like your client would.
One of the problems with this is that these tests can take a while to run, depending on how many you have. It also requires the server be up and running.
I know there must be better ways, but this has worked for our project for about 10 years.
Related
I have a moderately heavy springboot service, it takes 10-15 seconds to boot on a happy flow, and (1-2) minutes to fail on a retry/failover flow. This is ok for my business flows, and is how I expect a healthy service to behave.
I have integration tests (that run some end-to-end flows in my service), that can only test the actual integration status while the test machine (or dev machine) is connected to a specific VPN.
I want to auto skip integration tests if I'm not connected to VPN.
consider the following code
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {Server.class}, // auto scans a bunch of components
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) // slow loading context
public class IntegrationTest {
#BeforeClass
public static void beforeClass() {
Assume.assumeTrue(DnsTool.vpnConnected()); // fast failing test
}
#Test
public void testIntegration() {
// some test logic
}
}
When the assumptions pass, my tests run, and all is good.
When the assumptions fail, my tests get skipped, but only after trying to load my expensive context.
How can I avoid the long running time for my test suite?
Things I tried:
Subclassing SpringJUnit4ClassRunner, and overriding isTestMethodIgnored.
Adding a TestExecutionListener, and throwing the assumption exception in beforeTestClass
These made no impression on Spring, and the context got loaded any way.
Things I didn't try:
Lazy init comes with 2.2.X next stable release of spring I think.
Lazy init potentially makes my problem go away, but I feel like there should be some easy spring-test/junit fix that I'm missing.
Thanks in advance for the help.
To me, this sounds like something that you shouldn't do in tests at all.
Tests (at least IMHO), are supposed to check the business cases and assume that the environment is set up and ready.
Maybe it worth to delegate this functionality to build tool and CI.
Example:
Define a profile in maven (or whatever build tool you use) that will run integration tests that require VPN. Define profile that will run all the rest of integration tests as well.
Activate the profile if some system property is available.
In CI tool (like Jenkins) as a part of CI even before you run maven, run the script that will check the VPN connection. Based on the results set the system properties and run maven with these properties. The required profiles will be loaded and all the tests / only tests that do not require VPN will be run.
Update
If you need to make it work from Spring (and it looks like you prefer this way),
Spring has a special annotation called #IfProfileValue
By default, it matches against system properties and if the value doesn't match the test gets ignored.
It looks something like this (and note that you can put this annotation on class as well, then it will work for all test methods in the class):
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyTestClass {
#IfProfileValue(name = "os.name", values = {"Linux"})
#Test
public void testMe() {
// will run only in linux, otherwise
// won't even try to load an
// application context
....
}
}
This covers the case when you resolve the VPN connectivity externally and run the tests with a property. However, if you want to implement the VPN connectivity check in java, this annotation along not enough because it can work only with Java system properties, so in order to work with custom logic you need to implement org.springframework.test.annotation.ProfileValueSource:
public class VPNConnectivityProfileValueSource implements ProfileValueSource {
private String vpnEnabled = "true";
public VPNConnectivityProfileValueSource () {
// no spring context is available here
ClassPathResource resource = new ClassPathResource("vpn-config.properties");
if (resource.exists()) {
// read the VPN address,
//
//this.testProps = PropertiesLoaderUtils.loadProperties(resource);
// invoke your utility, check the connectivity, etc.
this.vpnEnabled = ...
}
}
#Override
public String get(String key) {
// this is important method,
if(key.equals("vpn.enabled") {
return this.vpnEnabled;
}
else return System.getProperty(key);
}
}
The last thing is to make the test aware of the ProfileValueSource:
For this there is another special annotation that you put on the test:
#ProfileValueSourceConfiguration(VPNConnectivityProfileValueSource.class)
All in all it the test can look like this:
#RunWith(SpringRunner.class)
#SpringBootTest
#ProfileValueSourceConfiguration(VPNConnectivityProfileValueSource.class)
#IfProfileValue(name = "vpn.enabled", value = "true")
public class MyTestClass {
#Test
public void testMe() {
....
}
}
All the classes/annotations I've mentioned reside in package org.springframework.test.annotation
I have to create project with interface(IGraphRemote), sessionbean(Graph) and client class(AppClient)
I tried to prepare simple project with one method register(int hwork, String album) - hwork its my homework's number, album its my album's number, but it doesn't work and returns an error: java.lang.NullPointerException , so connection wasn't established. How can i fix that ?
My files:
IGraphRemote
public interface IGraphRemote {
public boolean register(int hwork, String album);
}
Graph
#Remote
public class Graph implements IGraphRemote{
public boolean register(int hwork, String album) {
return (hwork == 6
&& album.equals("119962"));
}
}
And AppClient
public class AppClient {
#EJB
private static IGraphRemote gremote;
public static void main(String[] args) {
AppClient ap = new AppClient();
System.out.println(ap.gremote.register(6, "119962"));
}
}
here are my project
Error:
Exception in thread "main" java.lang.NullPointerException
at AppClient.main(AppClient.java:22)
Java Result: 1
It's obvious from your code and also in the stack trace that you try to run a standard j2se application with a main method from within a web application, so how do you expect dependcy injection to happen unless there is some other code create the ejb instance and inject it in the ejb reference. EJB is a J2EE component and it can't be created in standard java application, you need an ejb container with a naming service to manage the EJB lifecycle. You can create a standard j2se client by connecting to the naming service and lookup for the bean, but your EJB MUST be deployed in an ejb container in the first place such as TomEE, Glassfish, websphere,..etc
First of all, your EJB bean can not be static, waht is desrcibed here:
Injecting a static EJB, nonsense?
Second, if you want to inject your EJB bean, your app has to run in EJB container, like Glassfish or JBoss. If you run your app like simple Java SE applicaiton, your IGraphRemote gremote will always be null unless you initialize it by yourself.
I'm trying to wrap my head around Arquillian and perhaps even start using it in my project. I have a simple Java web app that deploys as a WAR to Tomcat.
In my project, I define a ServletContextListener impl so that I can execute code when Tomcat starts and stops the application.
I'm trying to write a super-simple Arquillian test class that uses ShrinkWrap and:
Confirms that my bundled WAR can be deployed to Tomcat and started without throwing exceptions; and
Can access a simple system property once the app is running (that the ServletContextListener checks for); and
Confirms that when Tomcat shuts down, no exceptions are thrown (clean shutdown)
Also, my class that implements ServletContextListener is called AppLifecycleManager:
public class AppLifeCycleManager implements ServletContextListener {
private String logLevel;
// Injected by Guice, but that's not really relevant for this question.
#Inject
private Logger logger;
// Getter and setter for logLevel and logger
#Override
public void contextInitialized(ServletContextEvent event) {
logLevel = System.getProperty("log.level");
}
#Override
public void contextDestroyed(ServletContextEvent event) {
logger.info("Peacefully shutting down the application.");
}
}
So far, here's my best attempt:
#RunWith(Arquillian.class)
public class MyFirstRealIntegrationTest {
#Deployment
public static Archive<?> createDeployment() {
// Haven't figured this part out yet, but for the sake of
// this question lets pretend this returns a properly-packaged
// WAR of my web app, the same that my Ant build currently produces.
}
#Test
public void shouldBeAbleToStartTomcatWithoutExceptions() {
// Given
Archive war = createDeployment();
// When - deploy war to Tomcat container
try {
// ??? how to access/init a Tomcat container?
TomcatContainer tomcat = new TomcatContainer(); // this is wrong
tomcat.start();
} catch(Throwable throwable) {
// Starting the container should not throw exceptions
Assert.fail();
}
}
#Test
public void shouldBeAbleToStopTomcatWithoutExceptions {
// Same setup as above test but stops tomcat and checks for
// thrown exceptions. Omitted for brevity.
}
#Test
public void shouldHaveAccessToSysPropsOnceRunning() {
// Here, deploy to the container and start it.
// Then, confirm that AppLifecycleManager correctly read
// the log.level system property.
// Given
Archive war = createDeployment();
TomcatContainer tomcat = new TomcatContainer();
// When - AppLifeycleManager should now read the system property
tomcat.start();
// Then - make sure log.level was set to "DEBUG" and that it was
// correctly read by AppLifeCycleManager.
Assert.assertTrue(war.getClass(AppLifeCycleManager.class)
.getLogLevel().equals("DEBUG"));
}
}
So, given my approach here, I immediately have several problems:
I'm not sure how to access/instantiate my Tomcat container so that it can even be started/stopped
I'm not sure how to actually execute tests from inside my running/deployed web app. In the 3rd test above I used war.getClass(AppLifeCycleManager.class).getLogLevel() to try and get access to a "live" class instance and check its logLevel property's runtime value, but I know this is wrong.
So I ask: how would a battle-worn Arquillian veteran write these 3 simple tests, and how do I actually go about performing tests on my "running" web app from inside the JUnit test? Thanks in advance.
I dont't think you should handle the startup/shutdown of tomcat in your test. I would by much easier if you used the embedded tomcat container: https://docs.jboss.org/author/display/ARQ/Tomcat+7.0+-+Embedded. In this case, arquillian will handle the startup and shutdown of tomcat.
You should create the deployment in the method annotated with #Deployment. Just read the following guide: http://arquillian.org/guides/getting_started/
You're almost there. createDeployment manages the lifecycle of your embedded container for you (starts and stops the virtual container automatically). That way, you're just focussing on the tests themselves. To write your integration tests, there's no "framework" or "Arquillian API" to code against. You just call classes and methods the way your main code does.
The key thing here is: your tests are actually running inside the container. If an exception occurs, or if your assert fails, the Arquillian runner throws an exception and stops the container. For your code example, where you're testing to see if you can read a system property:
#RunsWith(Arquillian.class)
public class MyArquillianTest {
#Deployment
public Archive<?> createDeployment() { ... }
#Test
public void shouldBeAbleToReadSysPropAtStartup() {
Assert.assertTrue(System.getProperty("log.level") != null);
}
}
Remember, Assert.assertTrue(System.getProperty("log.level") != null) Arquillian is "transporting" your code inside the container you configure it for. So that assert is actually running inside your deployed container.
I have managed to get alternate descriptors to work with my unit-tests running on OpenEJB using stubs for dependant EJB components, when each test is executed on their own. But once I introduce a test suite, it seems that deployment descriptor is taken from the first test added to the suite.
Some code to explain it better. Beans under test are something like
#Stateless
#Local(A.class)
public class ABean implements A {
// Bean implementation, no dependencies
}
#Stateless
#Local(B.class)
public class BBean implements B {
#EJB
A aBean; // Dependency to ABean
// Rest of the implementation
}
And testcase for B (testcase for A is similar, except it does not set the property for using alternate descriptor)
public class BBeanTest {
private B bean;
#Before
public void bootContainer() throws Exception {
Properties props = new Properties();
props.put(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.openejb.client.LocalInitialContextFactory");
props.put("openejb.altdd.prefix", "test"); // Use stubs
System.out.println("boot B: " + props);
context = new InitialContext(props);
bean = (B) context.lookup("BBeanLocal");
}
}
And as said, this all works just fine when executed alone. The alternate descriptor injects a stub implementation of A interface.
When using the following test suite, things start to fall apart.
#RunWith(Suite.class)
#Suite.SuiteClasses({
ABeanTest.class,
BBeanTest.class
})
public class MySuite {
// Empty on purpose, annotations do the trick
}
When running this suite, the alternate descriptor for testing B is not taken into use. Although, the output shows that at least the property is set before each test
boot A: {java.naming.factory.initial=org.apache.openejb.client.LocalInitialContextFactory}
boot A: {java.naming.factory.initial=org.apache.openejb.client.LocalInitialContextFactory}
boot A: {java.naming.factory.initial=org.apache.openejb.client.LocalInitialContextFactory}
boot B: {java.naming.factory.initial=org.apache.openejb.client.LocalInitialContextFactory, openejb.altdd.prefix=test}
boot B: {java.naming.factory.initial=org.apache.openejb.client.LocalInitialContextFactory, openejb.altdd.prefix=test}
If I reverse the order of loading tests to suite, i.e. add BBeanTest.class before ABeanTest.class, it'll use the alternate descriptor. As the ABean has no dependencies, this'll work fine in this case, but probably causes problems with bigger setups with multiple alternate descriptors.
Any pointers?
Thanks in advance.
EDIT Based on the log output, the container is actually booted only once for the first test as it takes approx. 2,5 seconds to execute while the others take around 0,001 seconds.
EDIT2 OpenEJB version is Apache OpenEJB 3.1.4 build: 20101112-03:32
Based on the log output, the container is actually booted only once for the first test as it takes approx. 2,5 seconds to execute while the others take around 0,001 seconds.
As you rightly noticed, the initialization happens only once.
#RunWith(Suite.class)
#Suite.SuiteClasses({
ABeanTest.class,
BBeanTest.class
})
Hence in this case, both ABeanTest and BBeanTest ran within the same container instance, with the same initial context properties as set by ABeanTest.
In your case, since you need different settings for the two test classes, I think dumping the container instance at ABeanTest #AfterClass and using a new one in BBeanTest should do it.
This blog post shows how
Note that I'm mirroring the example given here very closely.
In fact, my situation is somewhat simpler as I'm not even testing with a persistence unit at this point. My test project provides a simple MDB and a session bean; both the MDB and the session bean are getting loaded as normal, and can be successfully tested (in a constrained fashion) without injection.
The suggested injection with the #LocalClient annotation on my unit tests is failing with the known error:
javax.naming.NamingException: Unable to find injection meta-data for [your-class]. Ensure that class was annotated with #org.apache.openejb.api.LocalClient and was successfully discovered and deployed. See http://openejb.apache.org/3.0/local-client-injection.html
When I visit this page it informs me that I may need to add an extra property to my test case context setup. So that now looks like:
#Override
public void setUp() throws Exception {
initializeContext();
}
public void initializeContext() {
Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory");
// the property i've added
p.put("openejb.tempclassloader.skip", "annotations");
try {
InitialContext initialContext = new InitialContext(p);
initialContext.bind("inject", this);
} catch (Throwable throwable) {
throwable.printStackTrace();
throw new RuntimeException(throwable);
}
}
But it's still failing. I really like this idiom and would be very excited if I could successfully use it in my projects.
A few other notes:
I am providing an 'empty' ejb-jar.xml (in src/main/resources) and an application-client.xml (in src/test/resources) as suggested by Apache to tell OpenEJB to scan the classpath [UPDATE: as it turns out, I was doing this wrong. See my answer below for the suggestion that worked for me.]
The test cases annotated with #LocalClient aren't identified by the OpenEJB engine as actually getting picked up and processed properly (as my MDBs are, for example)
Thanks in advance for any help or guidance.
This issue is likely caused by improper location of the descriptors which hint OpenEJB which sorts of modules are available.
To ensure the test-classes get picked up properly, make sure you're placing a file named application-client.xml at src/test/resources/META-INF with the following content:
<application-client/>
This should force OpenEJB to scan and react to the presence of #LocalClient annotations.
I had a similar issue when I tried to test stuff in a test project called tomee-embedded-trial and it turned out that openejb ignores stuff called tomee-.* .
I fixed it for me by specifying the following system properties:
openejb.deployments.classpath.include=".*-trial.*" openejb.deployments.package.include=".*-trial.*"