Testovanie jarnej dávkovej úlohy
1. Úvod
Na rozdiel od iných jarných aplikácií prichádza testovanie dávkových úloh s niektorými konkrétnymi výzvami, väčšinou kvôli asynchrónnej povahe spôsobu vykonávania úloh.
V tomto výučbe sa chystáme preskúmať rôzne alternatívy testovania úlohy Spring Batch.
2. Požadované závislosti
Používame jar-topánka-štartér-dávka, tak si najskôr nastavíme požadované závislosti v našom pom.xml:
org.springframework.boot spring-boot-starter-batch 2.1.9.RELEASE org.springframework.boot spring-boot-starter-test 2.1.9.RELEASE test org.springframework.batch spring-batch-test 4.2.0.RELEASE test
Zaradili sme jar-boot-štartovací test a jarný dávkový test ktoré prinášajú niekoľko potrebných pomocných metód, poslucháčov a bežcov na testovanie aplikácií Spring Batch.
3. Definovanie jarnej dávkovej úlohy
Vytvorme si jednoduchú aplikáciu, ktorá ukáže, ako Spring Batch rieši niektoré z testovacích výziev.
Naša aplikácia využíva dva kroky Job ktorý číta vstupný súbor CSV so štruktúrovanými informáciami o knihe a vydáva knihy a podrobnosti knihy.
3.1. Definovanie pracovných krokov
Dva nasledujúce Kroks extrahovať konkrétne informácie z BookRecords a potom ich namapujte na Knihas (krok 1) a BookDetails (krok 2):
@Bean public Krok step1 (ItemReader csvItemReader, ItemWriter jsonItemWriter) vyvolá IOException {return stepBuilderFactory .get ("step1"). chunk (3) .reader (csvItemReader) .processor (bookItemProcessor ()) .writer (jsonItemWriter) .build (); } @Bean public Step step2 (ItemReader csvItemReader, ItemWriter listItemWriter) {return stepBuilderFactory .get ("step2"). chunk (3) .reader (csvItemReader) .processor (bookDetailsItemProcessor ()) .writer (listItemWriter) .build (); }
3.2. Definovanie vstupnej čítačky a výstupnej zapisovateľky
Poďme teraz nakonfigurovať vstupnú čítačku súborov CSV pomocou a FlatFileItemReader na serializáciu štruktúrovaných informácií o knihe BookRecord objekty:
private static final String [] TOKENS = {"bookname", "bookauthor", "bookformat", "isbn", "publishyear"}; @Bean @StepScope public FlatFileItemReader csvItemReader (@Value ("# {jobParameters ['file.input']}") reťazcový vstup) {FlatFileItemReaderBuilder builder = nový FlatFileItemReaderBuilder (); FieldSetMapper bookRecordFieldSetMapper = nový BookRecordFieldSetMapper (); return builder .name ("bookRecordItemReader") .resource (new FileSystemResource (input)) .delimited () .names (TOKENS) .fieldSetMapper (bookRecordFieldSetMapper) .build (); }
V tejto definícii je niekoľko dôležitých vecí, ktoré budú mať dôsledky na spôsob, akým testujeme.
Po prvé, anotovali sme FlatItemReader fazuľa s @StepScope, a ako výsledok, tento objekt bude zdieľať svoju životnosť s StepExecution.
To nám tiež umožňuje vložiť dynamické hodnoty za behu, aby sme mohli odovzdať náš vstupný súbor z JobParameters v riadku 4. Naproti tomu tokeny použité pre BookRecordFieldSetMapper sú nakonfigurované v čase kompilácie.
Potom podobne definujeme JsonFileItemWriter výstupný zapisovač:
@Bean @StepScope public JsonFileItemWriter jsonItemWriter (@Value ("# {jobParameters ['file.output']}") výstup reťazca) vyvolá IOException {JsonFileItemWriterBuilder builder = nový JsonFileItemWriterBuilder (); JacksonJsonObjectMarshaller marshaller = nový JacksonJsonObjectMarshaller (); return builder .name ("bookItemWriter") .jsonObjectMarshaller (marshaller) .resource (new FileSystemResource (output)) .build (); }
Na druhú Krok, používame jarnú dávku ListItemWriter ktorý jednoducho vypíše veci do zoznamu v pamäti.
3.3. Definovanie zvyku JobLauncher
Ďalej deaktivujeme predvolené nastavenie Job spustenie konfigurácie Spring Boot Batch nastavením spring.batch.job.enabled = false v našom application.properties.
Konfigurujeme si svoje vlastné JobLauncher odovzdať zvyk JobParameters napríklad pri spustení Job:
@SpringBootApplication verejná trieda SpringBatchApplication implementuje CommandLineRunner {// autowired jobLauncher a transformBooksRecordsJob @Value ("$ {file.input}") súkromný vstupný reťazec; @Value ("$ {file.output}") súkromný výstup reťazca; @Override public void run (String ... args) vyvolá výnimku {JobParametersBuilder paramsBuilder = nový JobParametersBuilder (); paramsBuilder.addString ("file.input", vstup); paramsBuilder.addString ("file.output", výstup); jobLauncher.run (transformBooksRecordsJob, paramsBuilder.toJobParameters ()); } // ďalšie metódy (hlavné atď.)}
4. Testovanie jarnej dávkovej úlohy
The jarný dávkový test Závislosť poskytuje sadu užitočných pomocných metód a poslucháčov, ktoré možno použiť na konfiguráciu kontextu Spring Batch počas testovania.
Vytvorme základnú štruktúru nášho testu:
@RunWith (SpringRunner.class) @SpringBatchTest @EnableAutoConfiguration @ContextConfiguration (classes = {SpringBatchConfiguration.class}) @TestExecutionListeners ({DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionList.class} ďalšie testovacie konštanty @Autowired private JobLauncherTestUtils jobLauncherTestUtils; @Autowired private JobRepositoryTestUtils jobRepositoryTestUtils; @After public void cleanUp () {jobRepositoryTestUtils.removeJobExecutions (); } súkromné JobParameters defaultJobParameters () {JobParametersBuilder paramsBuilder = nový JobParametersBuilder (); paramsBuilder.addString ("file.input", TEST_INPUT); paramsBuilder.addString ("file.output", TEST_OUTPUT); vrátiť paramsBuilder.toJobParameters (); }
The @SpringBatchTest anotácia poskytuje JobLauncherTestUtils a JobRepositoryTestUtils triedy pomocníkov. Používame ich na spustenie Job a Kroks v našich testoch.
Naša aplikácia používa Automatická konfigurácia Spring Boot, ktorá umožňuje predvolenú pamäť JobRepository. Ako výsledok, spustenie viacerých testov v tej istej triede si vyžaduje krok čistenia po každom spustení testu.
Nakoniec ak chceme spustiť viac testov z niekoľkých testovacích tried, musíme náš kontext označiť ako špinavý. Je to potrebné, aby sa zabránilo stretu viacerých osôb JobRepository inštancie používajúce rovnaký zdroj údajov.
4.1. Testovanie typu End-to-End Job
Prvá vec, ktorú otestujeme, je kompletný end-to-end Job s malým vstupom súboru údajov.
Potom môžeme výsledky porovnať s očakávaným výstupom testu:
@Test public void givenReferenceOutput_whenJobExecuted_thenSuccess () vyvolá výnimku {// daný FileSystemResource expectResult = nový FileSystemResource (EXPECTED_OUTPUT); FileSystemResource actualResult = nový FileSystemResource (TEST_OUTPUT); // keď JobExecution jobExecution = jobLauncherTestUtils.launchJob (defaultJobParameters ()); JobInstance actualJobInstance = jobExecution.getJobInstance (); ExitStatus actualJobExitStatus = jobExecution.getExitStatus (); // potom assertThat (actualJobInstance.getJobName (), je ("transformBooksRecords")); assertThat (actualJobExitStatus.getExitCode (), je ("DOKONČENÉ")); AssertFile.assertFileEquals (expectResult, actualResult); }
Spring Batch Test poskytuje užitočné metóda porovnania súborov na overovanie výstupov pomocou AssertFile trieda.
4.2. Testovanie jednotlivých krokov
Niekedy je dosť nákladné otestovať kompletnú verziu Job end-to-end, a preto má zmysel testovať jednotlivca Kroky namiesto toho:
@Test public void givenReferenceOutput_whenStep1Executed_thenSuccess () vyvolá výnimku {// daný FileSystemResource expectResult = nový FileSystemResource (EXPECTED_OUTPUT); FileSystemResource actualResult = nový FileSystemResource (TEST_OUTPUT); // keď JobExecution jobExecution = jobLauncherTestUtils.launchStep ("step1", defaultJobParameters ()); Zbierka actualStepExecutions = jobExecution.getStepExecutions (); ExitStatus actualJobExitStatus = jobExecution.getExitStatus (); // potom assertThat (actualStepExecutions.size (), je (1)); assertThat (actualJobExitStatus.getExitCode (), je ("DOKONČENÉ")); AssertFile.assertFileEquals (expectResult, actualResult); } @Test public void whenStep2Executed_thenSuccess () {// when JobExecution jobExecution = jobLauncherTestUtils.launchStep ("step2", defaultJobParameters ()); Zbierka actualStepExecutions = jobExecution.getStepExecutions (); ExitStatus actualExitStatus = jobExecution.getExitStatus (); // potom assertThat (actualStepExecutions.size (), je (1)); assertThat (actualExitStatus.getExitCode (), je ("DOKONČENÉ")); actualStepExecutions.forEach (stepExecution -> {assertThat (stepExecution.getWriteCount (), je (8));}); }
Všimni si používame launchStep metóda na spustenie konkrétnych krokov.
Zapamätaj si to navrhli sme aj naše ItemReader a ItemWriter používať dynamické hodnoty za behu, čo znamená môžeme odovzdať naše I / O parametre do Vykonanie úlohy(riadky 9 a 23).
Po prvýkrát Krok test, porovnávame skutočný výstup s očakávaným výstupom.
Na druhej strane, v druhom teste overujeme StepExecution za očakávané písomné položky.
4.3. Testovanie komponentov s obmedzeným rozsahom
Poďme teraz otestovať FlatFileItemReader. Pripomeňme, že sme to vystavili ako @StepScope fazuľa, takže na to budeme chcieť využiť špecializovanú podporu Spring Batch:
// predtým autowired itemReader @Test public void givenMockedStep_whenReaderCalled_thenSuccess () vyvolá výnimku {// daný StepExecution stepExecution = MetaDataInstanceFactory .createStepExecution (defaultJobParameters ()); // when StepScopeTestUtils.doInStepScope (stepExecution, () -> {BookRecord bookRecord; itemReader.open (stepExecution.getExecutionContext ()); while ((bookRecord = itemReader.read ())! = null) {// then assertThat (bookRecord .getBookName (), is ("Foundation")); assertThat (bookRecord.getBookAuthor (), is ("Asimov I.")); assertThat (bookRecord.getBookISBN (), is ("ISBN 12839")); assertThat ( bookRecord.getBookFormat (), je ("pevná väzba")); assertThat (bookRecord.getPublishingYear (), je ("2018"));} itemReader.close (); return null;}); }
The MetadataInstanceFactory vytvára zvyk StepExecution to je potrebné na vpichnutie nášho Step-scoped ItemReader.
Kvôli tomu, môžeme skontrolovať správanie čitateľa pomocou doInTestScope metóda.
Ďalej otestujeme JsonFileItemWriter a overiť jeho výstup:
@Test public void givenMockedStep_whenWriterCalled_thenSuccess () vyvolá výnimku {// daný FileSystemResource expectResult = nový FileSystemResource (EXPECTED_OUTPUT_ONE); FileSystemResource actualResult = nový FileSystemResource (TEST_OUTPUT); Demo kniha = nová kniha (); demoBook.setAuthor ("Grisham J."); demoBook.setName ("firma"); StepExecution stepExecution = MetaDataInstanceFactory .createStepExecution (defaultJobParameters ()); // when StepScopeTestUtils.doInStepScope (stepExecution, () -> {jsonItemWriter.open (stepExecution.getExecutionContext ()); jsonItemWriter.write (Arrays.asList (demoBook)); jsonItemWriter.close (); return null; // potom AssertFile.assertFileEquals (expectResult, actualResult); }
Na rozdiel od predchádzajúcich testov teraz máme úplnú kontrolu nad našimi testovacími objektmi. Ako výsledok, sme zodpovední za otváranie a zatváranie I / O streamov.
5. Záver
V tomto tutoriáli sme preskúmali rôzne prístupy testovania úlohy Spring Batch.
End-to-end testovanie overuje úplné vykonanie úlohy. Testovanie jednotlivých krokov môže pomôcť v zložitých scenároch.
Nakoniec, pokiaľ ide o komponenty s rozsahovým rozsahom, môžeme použiť množstvo pomocných metód poskytovaných používateľom jarný dávkový test. Pomôžu nám pri zatĺkaní a zosmiešňovaní objektov domény Spring Batch.
Ako obvykle, môžeme preskúmať celú základňu kódov na GitHub.