Очевидно, что один из подходов заключается в том, чтобы изменить его таким образом, чтобы вы могли внедрять такие вещи, как FlexContext.Однако, это не всегда возможно.Некоторое время назад команда, в которой я участвовал, попала в ситуацию, когда нам пришлось смоделировать некоторые вещи внутреннего класса, к которым у нас не было доступа (например, ваш контекст).В итоге мы использовали API под названием jmockit , который позволяет эффективно макетировать отдельные методы, включая статические вызовы.
Используя эту технологию, мы смогли обойти очень грязную реализацию сервера, и вместо того, чтобы развернуть ее на живых серверах и в тесте черного ящика, мы смогли провести модульное тестирование на хорошем уровне, переопределив серверную технологию, котораябыло эффективно жестко закодировано.
Единственная рекомендация, которую я хотел бы сделать по поводу использования чего-то вроде jmockit, - убедиться, что в вашем тестовом коде есть четкая документация и отделение jomockit от вашей основной среды разработки ( easymock или mockito были бы моими рекомендациями).В противном случае вы рискуете запутать разработчиков в отношении различных обязанностей каждой части головоломки, что обычно приводит к некачественным тестам или тестам, которые работают не так хорошо.В идеале, как мы и сделали, оберните код jmockit в свои тестовые устройства, чтобы разработчики даже не знали об этом.Для большинства людей достаточно иметь 1 api.
Просто черт возьми, вот код, который мы использовали для исправления тестирования для класса IBM.Нам в основном нужно сделать две вещи:
- Иметь возможность вводить собственные макеты, которые будут возвращены методом.
- Убить конструктор, который отправился на поиск работающего сервера.
- Выполните вышеописанное, не имея доступа к исходному коду.
Вот код:
import java.util.HashMap;
import java.util.Map;
import mockit.Mock;
import mockit.MockClass;
import mockit.Mockit;
import com.ibm.ws.sca.internal.manager.impl.ServiceManagerImpl;
/**
* This class makes use of JMockit to inject it's own version of the
* locateService method into the IBM ServiceManager. It can then be used to
* return mock objects instead of the concrete implementations.
* <p>
* This is done because the IBM implementation of SCA hard codes the static
* methods which provide the component lookups and therefore there is no method
* (including reflection) that developers can use to use mocks instead.
* <p>
* Note: we also override the constructor because the default implementations
* also go after IBM setup which is not needed and will take a large amount of
* time.
*
* @see AbstractSCAUnitTest
*
* @author Derek Clarkson
* @version ${version}
*
*/
// We are going to inject code into the service manager.
@MockClass(realClass = ServiceManagerImpl.class)
public class ServiceManagerInterceptor {
/**
* How we access this interceptor's cache of objects.
*/
public static final ServiceManagerInterceptor INSTANCE = new ServiceManagerInterceptor();
/**
* Local map to store the registered services.
*/
private Map<String, Object> serviceRegistry = new HashMap<String, Object>();
/**
* Before runnin your test, make sure you call this method to start
* intercepting the calls to the service manager.
*
*/
public static void interceptServiceManagerCalls() {
Mockit.setUpMocks(INSTANCE);
}
/**
* Call to stop intercepting after your tests.
*/
public static void restoreServiceManagerCalls() {
Mockit.tearDownMocks();
}
/**
* Mock default constructor to stop extensive initialisation. Note the $init
* name which is a special JMockit name used to denote a constructor. Do not
* remove this or your tests will slow down or even crash out.
*/
@Mock
public void $init() {
// Do not remove!
}
/**
* Clears all registered mocks from the registry.
*
*/
public void clearRegistry() {
this.serviceRegistry.clear();
}
/**
* Override method which is injected into the ServiceManager class by
* JMockit. It's job is to intercept the call to the serviceManager's
* locateService() method and to return an object from our cache instead.
* <p>
* This is called from the code you are testing.
*
* @param referenceName
* the reference name of the service you are requesting.
* @return
*/
@Mock
public Object locateService(String referenceName) {
return serviceRegistry.get(referenceName);
}
/**
* Use this to store a reference to a service. usually this will be a
* reference to a mock object of some sort.
*
* @param referenceName
* the reference name you want the mocked service to be stored
* under. This should match the name used in the code being tested
* to request the service.
* @param serviceImpl
* this is the mocked implementation of the service.
*/
public void registerService(String referenceName, Object serviceImpl) {
serviceRegistry.put(referenceName, serviceImpl);
}
}
А вот абстрактный класс, который мы использовали в качестве родителя длятесты.
public abstract class AbstractSCAUnitTest extends TestCase {
protected void setUp() throws Exception {
super.setUp();
ServiceManagerInterceptor.INSTANCE.clearRegistry();
ServiceManagerInterceptor.interceptServiceManagerCalls();
}
protected void tearDown() throws Exception {
ServiceManagerInterceptor.restoreServiceManagerCalls();
super.tearDown();
}
}