Статьи

OSGi: тестовый расширитель Junit с использованием Fragment и BundleTracker

Введение :

Как сервисный трекер, в
выпуске Osgi 4.2 был представлен «BundleTracker», который упрощает работу с пакетами и облегчает построение расширений. В этой статье мы попытаемся написать простой тестовый расширитель JUnit, используя фрагменты (чтобы отделить тесты от исходного кода). Мы дадим пользователю возможность автоматизировать модульное тестирование сразу после или после установки пакета фрагментов, а также консольную команду для тестирования всех установленных фрагментов или конкретного фрагмента.

II- BundleTracker

Мы будем использовать BundleTrackerCustomizer для отслеживания только состояния Resolved Bundle, поскольку фрагменты не будут находиться в начальном состоянии, и чтобы убедиться, что этот фрагмент имеет разрешенный родительский элемент. 

bundleTracker = new BundleTracker(context, Bundle.RESOLVED, testExtender);
bundleTracker.open();

где TestExtender

1 — реализует BundleTrackerCustomizer

2 — проверяет, является ли он фрагментом теста

        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- Если да, добавьте (или обновите) его на карту, используя его идентификатор пакета.

 private Map bundles = Collections.synchronizedMap(new HashMap());
bundles.put(bundle.getBundleId(), bundle);

4 — проверьте, включен ли автоматический тест:

 public static final boolean isAutoTestEnabled(Bundle bundle) {
return "true".equals(bundle.getHeaders().get(TEST_HEADER) + "");
}
if (isAutoTestEnabled(bundle)) {
test(bundle.getBundleId());
}

III- Тест

Давайте определим метод loadClass для загрузки класса из bundle и getHostBundle для получения родительского 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 и getHostBundle будут использоваться для загрузки всего тестового класса из фрагмента, используя контекст hostBundle !!!! (фрагмент будет загружен с использованием загрузчика контекста хоста) и попытаться рекурсивно загрузить весь класс с суффиксом Test.

 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;
}

для каждого загруженного тестового класса мы проверяем его, чтобы найти аннотированные методы junit:

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;
}

а затем эмулировать механизм junit с помощью рефлексии:

  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("___________________________________________________________________________");
}

объект, переданный этому методу, является новым экземпляром класса, поэтому не забывайте (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 повторяет все зарегистрированные тесты.

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

IV- Консоль

чтобы дать пользователю возможность протестировать определенный фрагмент или выполнить все тесты нашего активатора, зарегистрируйте сервис CommandProvider с 3 запускающимися методами (_):

 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;
}

(и _helpTest, а не -help, чтобы сохранить помощь равноденствия тоже)

@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-фрагмент

наш фрагмент содержит 2 класса один для тестирования (OneTest):

import static org.junit.Assert.*;
public class OneTest {

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

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


с явными заголовками:

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

Выходные данные:

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]

___________________________________________________________________________

Метод: [fail] ОШИБКА

Метод: [echo] PASS

___________________________________________________________________________

вывод :

Надеюсь, что эта статья поможет вам проверить ваши пакеты, используя чистый фрагмент. Проект может быть экспортирован в версию 4.1 с использованием слушателей, но BundleTracker очень полезен.

исходный код доступен в кенае:
http://kenai.com/projects/testosgifragment