Статьи

Spring Integration File Polling и тесты

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

Spring Integration идеально подходит для этого требования, поскольку поставляется с канальным адаптером, который может сканировать папку на наличие новых файлов и затем передавать файл через поток сообщений.

Цель этой конкретной статьи — узнать, как можно протестировать поток файловых программ.

Для начала рассмотрим простой поток, реализованный с использованием следующей конфигурации интеграции Spring:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
    .....
 <channel id="fileChannel"></channel>
 <channel id="processedFileChannel"></channel>
 
 <int-file:inbound-channel-adapter directory="${inbound.folder}" channel="fileChannel" filename-pattern="*.*">
  <poller fixed-delay="5000"></poller>
 </int-file:inbound-channel-adapter>
 
 <service-activator input-channel="fileChannel" ref="fileHandlerService" method="handle" output-channel="processedFileChannel">
 </service-activator>
 
 <logging-channel-adapter id="loggingChannelAdapter" channel="processedFileChannel"></logging-channel-adapter>
 
 <beans:bean name="fileHandlerService" class="files.RealFileHandlingService"/>
 <context:property-placeholder properties-ref="localProps" local-override="true"/>
 
 <util:properties id="localProps">
 </util:properties>
</beans:beans>

Это лучше представлено на диаграмме:

Простое тестирование потока

Теперь, чтобы протестировать этот поток, мой первый подход состоял в том, чтобы поместить образец файла в папку в classpath, динамически обнаружить и использовать это имя папки во время теста и записать окончательное сообщение в тестовый канал и подтвердить сообщения, поступающие в этот канал. , Вот как выглядит тестовая конфигурация, которая составляет исходную конфигурацию Spring:

01
02
03
04
05
06
07
08
09
10
11
12
    ....
 <beans:import resource="si-fileprocessing.xml"/>
 <util:properties id="localProps">
  <beans:prop key="inbound.folder">#{new java.io.File(T(java.io.File).getResource("/samplefolder/samplefile.txt").toURI()).parent}</beans:prop>
 </util:properties>
 <channel id="testChannel">
  <queue/>
 </channel>
 
 <bridge id="loggingChannelAdapter" input-channel="processedFileChannel" output-channel="testChannel"></bridge>
</beans:beans>

и тест выглядит так:

1
2
3
4
5
6
7
@Autowired @Qualifier("testChannel") PollableChannel testChannel;
 
@Test
public void testService() throws Exception{
 Message<?> message = this.testChannel.receive(5000);
 assertThat((String)message.getPayload(), equalTo("Test Sample file content"));
}

Это работает аккуратно, особенно обратите внимание, что путь может быть полностью указан в конфигурации с использованием выражения Spring-EL:

1
#{new java.io.File(T(java.io.File).getResource("/samplefolder/samplefile.txt").toURI()).parent}

Макет Сервиса

Теперь, если пойти немного дальше, что если я захочу издеваться над сервисом, обрабатывающим файл (files.RealFileHandlingService). Здесь есть проблема, которая станет понятной после того, как макет будет реализован. Подход к внедрению в mock-сервисе заключается в использовании Mockito и вспомогательной функции, которую он предоставляет для создания mock-файла таким образом с помощью файла конфигурации Spring:

1
2
3
<beans:bean name="fileHandlerService" factory-method="mock" class="org.mockito.Mockito">
 <beans:constructor-arg value="files.FileHandlingService"></beans:constructor-arg>
</beans:bean>

Как только этот фиктивный бин создан, его поведение можно добавить в аннотированный метод junit @Before следующим образом:

01
02
03
04
05
06
07
08
09
10
@Before
public void setUp() {
 when(fileHandlingServiceMock.handle((File)(anyObject()))).thenReturn("Completed File Processing");
}
 
@Test
public void testService() throws Exception{
 Message<?> message = this.testChannel.receive(5000);
 assertThat((String)message.getPayload(), equalTo("Completed File Processing"));
}

Если тест повторяется сейчас, неожиданно он не пройдёт, и причина в том, что контекст приложения Spring полностью инициализируется к тому времени, когда вызов приходит к методу @Before в Junit, и ПО, проверяющее файл, поступающий в папку, запускается ПЕРЕД макетом добавлено поведение и, следовательно, без корректного поведения фиктивной службы тест не пройден.

Я уверен, что есть и другие исправления, но исправление, которое работало для меня, состояло в том, чтобы по существу контролировать, какая папка сканируется и в какой момент файл помещается в папку для запуска потока. Это в значительной степени основано на некоторых тестах, которые я видел в самом проекте Spring Integration. Хитрость заключается в том, чтобы сначала создать временную папку, используя конфигурацию Spring, и установить ее в качестве папки опроса:

01
02
03
04
05
06
07
08
09
10
<beans:bean id="temp" class="org.junit.rules.TemporaryFolder"
   init-method="create" destroy-method="delete"/>
 
<beans:bean id="inputDirectory" class="java.io.File">
 <beans:constructor-arg value="#{temp.newFolder('sitest').path}"/>
</beans:bean>
 
<util:properties id="localProps">
 <beans:prop key="inbound.folder">#{inputDirectory.getAbsolutePath()}</beans:prop>
</util:properties>

Теперь поместите файл во временную папку только во время пробного запуска, таким образом, аннотированный метод @Before получает возможность добавить поведение к макету, и смоделированное поведение может быть четко подтверждено:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("si-test.xml")
public class FileHandlingFlowTest {
 @Autowired @Qualifier("testChannel") private PollableChannel testChannel;
 @Autowired private FileHandlingService fileHandlingServiceMock;
 @Autowired @Qualifier("inputDirectory") private File inputDirectory;
 
 @Before
 public void setUp() {
  when(fileHandlingServiceMock.handle((File)(anyObject()))).thenReturn("Completed File Processing");
 }
 
 @Test
 public void testService() throws Exception{
  Files.copy(new File(this.getClass().getResource("/samplefolder/samplefile.txt").toURI()), new File(inputDirectory, "samplefile.txt"));
  Message<?> message = this.testChannel.receive(5000);
  assertThat((String)message.getPayload(), equalTo("Completed File Processing"));
 }
}

Ссылка: Spring Integration File Polling и тесты от нашего партнера JCG Биджу Кунджуммена в блоге all and sundry.