У меня есть следующий устаревший код:
public class MyLegacyClass
{
private static final String jndiName = "java:comp/env/jdbc/LegacyDataSource"
public static SomeLegacyClass doSomeLegacyStuff(SomeOtherLegacyClass legacyObj)
{
// do stuff using jndiName
}
}
Этот класс работает в J2EE-контейнере.
Теперь я хочу протестировать класс вне контейнера.
Какая стратегия лучшая? Рефакторинг в основном разрешен.
Доступ к LegacyDataSource разрешен (тест не обязательно должен быть «чистым» модульным тестом).
Обновлено: введение дополнительных сред выполнения не допускается.




Выполните рефакторинг кода, чтобы использовать внедрение зависимостей. Затем используйте предпочитаемую вами структуру DI (Spring, Guice, ...) для внедрения ваших ресурсов. Это упростит переключение между объектами ресурсов и стратегиями во время выполнения.
В этом случае вы можете ввести свой источник данных.
Обновлено: в зависимости от вашего нового ограничения вы можете сделать то же самое, используя шаблон стратегии для установки источника данных во время выполнения. Вероятно, вы можете просто использовать файл свойств, чтобы определить, какую стратегию создавать и предоставлять источник данных. Для этого не потребуется новая структура, вы просто вручную кодируете те же базовые функции. Мы использовали эту точную идею с ServiceLocator для предоставления имитационного источника данных при тестировании вне контейнера Java EE.
Я думаю, что лучшее решение здесь - привязать этот JNDI к локальному
В устаревшем коде jndiName используется следующим образом:
DataSource datasource = (DataSource)initialContext.lookup(DATASOURCE_CONTEXT);
Итак, решение здесь - привязать локальные (или все, что у вас есть для тестовых данных) к JNDI следующим образом:
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(System.getProperty("driverClassName"));
dataSource.setUser("username");
dataSource.setPassword("password");
dataSource.setServerName("localhost");
dataSource.setPort(3306);
dataSource.setDatabaseName("databasename");
А потом привязка:
Context context = new InitialContext();
context.bind("java:comp/env/jdbc/LegacyDataSource",datasource);
Или что-то подобное, надеюсь, это вам поможет.
Удачи!
Просто чтобы сделать предложение @ Robin о шаблоне стратегии более конкретным: (Обратите внимание, что общедоступный API вашего исходного вопроса остается неизменным.)
public class MyLegacyClass {
private static Strategy strategy = new JNDIStrategy();
public static SomeLegacyClass doSomeLegacyStuff(SomeOtherLegacyClass legacyObj) {
// legacy logic
SomeLegacyClass result = strategy.doSomeStuff(legacyObj);
// more legacy logic
return result;
}
static void setStrategy(Strategy strategy){
MyLegacyClass.strategy = strategy;
}
}
interface Strategy{
public SomeLegacyClass doSomeStuff(SomeOtherLegacyClass legacyObj);
}
class JNDIStrategy implements Strategy {
private static final String jndiName = "java:comp/env/jdbc/LegacyDataSource";
public SomeLegacyClass doSomeStuff(SomeOtherLegacyClass legacyObj) {
// do stuff using jndiName
}
}
... и JUnit test. Я не большой поклонник необходимости выполнять эту настройку / демонтаж, но это неприятный побочный эффект наличия API, основанного на статических методах (или синглтонах, если на то пошло). Что мне делать нравится в этом тесте, так это то, что он не использует JNDI - это хорошо, потому что (а) он будет работать быстро, и (б) модульный тест в любом случае должен тестировать только бизнес-логику в методе doSomeLegacyStuff (), а не тестировать фактический источник данных. (Кстати, это предполагает, что тестовый класс находится в том же пакете, что и MyLegacyClass.)
public class MyLegacyClassTest extends TestCase {
private MockStrategy mockStrategy = new MockStrategy();
protected void setUp() throws Exception {
MyLegacyClass.setStrategy(mockStrategy);
}
protected void tearDown() throws Exception {
// TODO, reset original strategy on MyLegacyClass...
}
public void testDoSomeLegacyStuff() {
MyLegacyClass.doSomeLegacyStuff(..);
assertTrue(..);
}
static class MockStrategy implements Strategy{
public SomeLegacyClass doSomeStuff(SomeOtherLegacyClass legacyObj) {
// mock behavior however you want, record state however
// you'd like for test asserts. Good frameworks like Mockito exist
// to help create mocks
}
}
}
@ Scot Bale Просто спрашиваю, не может ли это привести к некоторому состоянию гонки и несоответствиям в многопоточной среде, например, если doSomeLegacyStuff привязан к сеансу пользователя
Я обновил свой ответ с учетом вашего нового ограничения. На самом деле у нас есть система, которая должна была решать ту же проблему.