DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Testing, Deployment, and Maintenance
  3. Testing, Tools, and Frameworks
  4. OSGi : Junit Test Extender Using Fragment and BundleTracker

OSGi : Junit Test Extender Using Fragment and BundleTracker

Slim Ouertani user avatar by
Slim Ouertani
·
Oct. 02, 09 · Interview
Like (1)
Save
Tweet
Share
13.31K Views

Join the DZone community and get the full member experience.

Join For Free

I-Introduction :

As Service tracker, the Osgi 4.2 release introduced "BundleTracker" which simplifies working with bundles and facilitate building extenders. In this article, we will try to write a simple JUnit test extender using fragments (to separate tests from source code). We will give to the user opportunities to automate the unit test just after or when the fragment bundle is installed, plus a console command to test all installed fragments or a specific one.

II- BundleTracker

We will use BundleTrackerCustomizer to track only Resolved Bundle state as fragments will not be at started state and to be sure that this fragment has a resolved parent. 
bundleTracker = new BundleTracker(context, Bundle.RESOLVED, testExtender);
bundleTracker.open();

where TestExtender
1 - implements BundleTrackerCustomizer
2- check if it is a test fragment
        private static final String TEST_HEADER = "Unit-Test";
public static final boolean isTestFragment(Bundle bundle) {
String header = bundle.getHeaders().get(TEST_HEADER) + "";
String fragment = bundle.getHeaders().get(org.osgi.framework.Constants.FRAGMENT_HOST) + "";
return (!"null".equals(header) && !"null".equals(fragment));
}
3- If yes, add (or update) it to map using it's bundle id.
 private Map bundles = Collections.synchronizedMap(new HashMap());
bundles.put(bundle.getBundleId(), bundle);

4- check if auto test is enabled :
 public static final boolean isAutoTestEnabled(Bundle bundle) {
return "true".equals(bundle.getHeaders().get(TEST_HEADER) + "");
}
if (isAutoTestEnabled(bundle)) {
test(bundle.getBundleId());
}

III- Test

Let's define loadClass method to load class from bundle and getHostBundle to get the parent Bundle
public static final Class loadClass(String clazz, Bundle bundleHost) {
try {
Class loadClass = bundleHost.loadClass(clazz);
return loadClass;

} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
 public static final Bundle getHostBundle(BundleContext context,Bundle bundle) {
String fragment = bundle.getHeaders().get(org.osgi.framework.Constants.FRAGMENT_HOST) + "";
Bundle[] bundles = context.getBundles();
for (Bundle ibundle : bundles) {
if (ibundle.getSymbolicName().equals(fragment)) {
return ibundle;
}
}
throw new RuntimeException();
}

loadClass and getHostBundle will be used to load all test class from fragment using hostBundle context!!!! (fragment will be loaded using it's host context loader) and try to load all class with Test suffix recursively.
 public static final List> getTestClass(BundleContext context,Bundle bundle) {
List> clazzs = new ArrayList>();
Enumeration entrs = bundle.findEntries("/", "*Test.class", true);
if (entrs == null || !entrs.hasMoreElements()) {
return Collections.EMPTY_LIST;
}
Bundle hostBundle = getHostBundle(context,bundle);
while (entrs.hasMoreElements()) {
URL e = (URL) entrs.nextElement();
String file = e.getFile();

String className = file.replaceAll("/", ".").replaceAll(".class", "").replaceFirst(".", "");
Class clazz = loadClass(className, hostBundle);
clazzs.add(clazz);
}
return clazzs;
}

for each loaded test Class we inspect it to find junit annotated methods :
public static final Test inspectClass(Class clazz) {
Test test = new Test();
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method method : declaredMethods) {

if (method.isAnnotationPresent(org.junit.BeforeClass.class)) {
test.setBeforeClass(method);
}
if (method.isAnnotationPresent(org.junit.AfterClass.class)) {
test.setAfterClass(method);
}
if (method.isAnnotationPresent(org.junit.Before.class)) {
test.setBefore(method);
}
if (method.isAnnotationPresent(org.junit.After.class)) {
test.setAfter(method);
}
if (method.isAnnotationPresent(org.junit.Test.class)) {
test.addTest(method);
}
}
return test;
}
and then, emulate junit mechanism using reflexion :
  public static void testClass(Test testClass, Object object) {
System.out.println("___________________________________________________________________________");


try {
try {
if (testClass.getBeforeClass() != null) {
testClass.getBeforeClass().invoke(object, new Object[0]);
}

List tests = testClass.getTests();
for (Method method : tests) {
try {
if (testClass.getBefore() != null) {
testClass.getBefore().invoke(object, new Object[0]);
}
try {
method.invoke(object, new Object[0]);
System.out.println("Method : [ "+ method.getName()+" ] PASS " );
} catch (Exception ex) {
System.out.println("Method : [ "+ method.getName()+" ] ERROR " );
}
} finally {
if (testClass.getAfter() != null) {
testClass.getAfter().invoke(object, new Object[0]);
}
}
}
} finally {
if (testClass.getAfterClass() != null) {
testClass.getAfterClass().invoke(object, new Object[0]);
}
}

} catch (Exception ex) {
ex.printStackTrace();
}
System.out.println("___________________________________________________________________________");
}

the object passed to this method is a new instance of class this is why don't forget ( DynamicImport-Package: *)
  for (Class clazz : testClazzs) {
try {
System.out.println("CLASS : ["+clazz.getName()+"]");
Test inspectClass = EClassUtils.inspectClass(clazz);
EClassUtils.testClass(inspectClass, clazz.newInstance());
} catch (Exception ex) {
ex.printStackTrace();
}

}
testAll loops all registered test.
public void testAll() {
Set> entrySet = bundles.entrySet();
System.out.println("===========================================================================");
for (Entry entry : entrySet) {
test(entry.getKey());
}
System.out.println("============================================================================");
}

IV- Console

to give the user opportunities to test specific fragment or do all test our activator register CommandProvider service with 3 methods starting (_) :
 public Object _test(CommandInterpreter intp) {
String nextArgument = intp.nextArgument();
testExtender.test(Long.parseLong(nextArgument));
return null;
}


public Object _testall(CommandInterpreter intp) {
testExtender.testAll();
return null;
}

public Object _helpTest(CommandInterpreter intp) {
String help = getHelp();
System.out.println(help);
return null;
}

(and _helpTest and not -help to conserve equinox help too)
@Override
public String getHelp() {
StringBuilder buffer = new StringBuilder();
buffer.append("---Testing commands---\n\t");
buffer.append("test [bundle id] - test bundle fragment id\n\t");
buffer.append("testall - test all fragments\n\t");
buffer.append("help - Print this help\n");
return buffer.toString();
}

V-Fragment

our fragment contains 2 class one is to be tested (OneTest):
import static org.junit.Assert.*;
public class OneTest {

@Test
public void echo() {
assertFalse(false);
}

@Test
public void fail() {
assertTrue(false);
}
}


with manifest headers :
Fragment-Host: com.jtunisie.osgi.client
Unit-Test: true

Output :

30      ACTIVE      com.jtunisie.osgi.test.extender_1.0.0.SNAPSHOT
31      ACTIVE      com.jtunisie.osgi.client_1.0.0.SNAPSHOT
                    Fragments=32
32      RESOLVED    com.jtunisie.osgi.fragment.test_1.0.0.SNAPSHOT
                    Master=31

osgi> test 32
Bundle : [32] : com.jtunisie.osgi.fragment.test
_
CLASS : [com.jtunisie.osgi.fragment.test.OneTest]
___________________________________________________________________________
Method : [ fail ] ERROR
Method : [ echo ] PASS
___________________________________________________________________________

conclusion :


Hope this is article help you to test your bundles using clean fragment. Project can be exported to 4.1 release using listeners but BundleTracker is very helpful .

source code is shared in kenai : http://kenai.com/projects/testosgifragment
unit test Fragment (logic) JUnit

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • How To Handle Secrets in Docker
  • Cloud Performance Engineering
  • Custom Validators in Quarkus
  • DevOps vs Agile: Which Approach Will Win the Battle for Efficiency?

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: