Недавно я реализовал небольшой проект, в котором мы должны были опрашивать папку для новых файлов, а затем запускать поток обслуживания для содержимого файла.
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" )); } } |