Testiranje proljetnog serijskog posla

1. Uvod

Za razliku od ostalih aplikacija temeljenih na Springu, testiranje batch poslova dolazi s određenim izazovima, uglavnom zbog asinkrone prirode izvršavanja poslova.

U ovom uputstvu istražit ćemo razne alternative za testiranje Spring Batch posla.

2. Potrebne ovisnosti

Koristimo opruga-boot-starter-batch, pa prvo postavimo potrebne ovisnosti u našem 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 

Uključili smo proljeće-boot-starter-test i proljetno-šaržno ispitivanje koji uključuju neke potrebne pomoćne metode, slušatelje i pokretače za testiranje Spring Batch aplikacija.

3. Definiranje proljetnog serijskog posla

Stvorimo jednostavnu aplikaciju koja će pokazati kako Spring Batch rješava neke od izazova testiranja.

Naša se aplikacija koristi u dva koraka Posao koji čita CSV ulaznu datoteku sa strukturiranim informacijama o knjizi i izbacuje knjige i detalje o knjizi.

3.1. Definiranje koraka posla

Dvije slijedeće Koraks izvlači određene podatke iz BookRecords, a zatim ih mapirajte u Knjigas (korak1) i BookDetails (korak 2):

@Bean javni korak step1 (ItemReader csvItemReader, ItemWriter jsonItemWriter) baca IOException {return stepBuilderFactory .get ("step1"). komad (3) .reader (csvItemReader) .procesor (bookItemProcessor ()) .writer (jsonItemWriter) .build (); } @Bean javni korak step2 (ItemReader csvItemReader, ItemWriter listItemWriter) {return stepBuilderFactory .get ("step2"). komad (3) .reader (csvItemReader) .procesor (bookDetailsItemProcessor ()) .writer (listItemWriter) .build (); }

3.2. Definiranje čitača ulaza i pisača izlaza

Idemo sada konfigurirajte čitač unosa CSV datoteke pomoću a FlatFileItemReader za uklanjanje serializacije strukturiranih podataka o knjizi u BookRecord objekti:

privatni statički završni niz [] TOKENS = {"ime knjige", "bookauthor", "format knjige", "isbn", "objavitigodine"}; @Bean @StepScope javni FlatFileItemReader csvItemReader (@Value ("# {jobParameters ['file.input']}") String input) {FlatFileItemReaderBuilder builder = new FlatFileItemReaderBuilder (); FieldSetMapper bookRecordFieldSetMapper = novi BookRecordFieldSetMapper (); povrat graditelja .name ("bookRecordItemReader") .resource (novi FileSystemResource (ulaz)) .delimited () .names (TOKENS) .fieldSetMapper (bookRecordFieldSetMapper) .build (); }

Postoji nekoliko važnih stvari u ovoj definiciji, koje će imati implikacije na način na koji testiramo.

Kao prvo, zabilježili smo FlatItemReader grah sa @StepScope, i kao rezultat, ovaj će objekt podijeliti svoj vijek trajanja StepExecution.

To nam također omogućuje ubrizgavanje dinamičkih vrijednosti tijekom izvođenja, tako da možemo proslijediti svoju ulaznu datoteku iz Parametar poslas u redu 4. Suprotno tome, žetoni koji se koriste za BookRecordFieldSetMapper konfiguriraju se u vrijeme prevođenja.

Zatim slično definiramo JsonFileItemWriter izlazni pisac:

@Bean @StepScope public JsonFileItemWriter jsonItemWriter (@Value ("# {jobParameters ['file.output']}") String output) baca IOException {JsonFileItemWriterBuilder builder = new JsonFileItemWriterBuilder (JsonFileItemWriterBuilder); JacksonJsonObjectMarshaller marshaller = novi JacksonJsonObjectMarshaller (); return builder .name ("bookItemWriter") .jsonObjectMarshaller (marshaller) .resource (novi FileSystemResource (izlaz)) .build (); } 

Za drugo Korak, koristimo Spring Batch ListItemWriter koji samo baca stvari na popis u memoriji.

3.3. Definiranje običaja Pokretač poslova

Dalje, onemogućimo zadano Posao pokretanje konfiguracije Spring Boot Batch postavljanjem spring.batch.job.enabled = false u našem primjena.svojstva.

Konfiguriramo svoje Pokretač poslova donijeti običaj Parametri posla instanci prilikom pokretanja Posao:

@SpringBootApplication javna klasa SpringBatchApplication implementira CommandLineRunner {// autowired jobLauncher i transformBooksRecordsJob @Value ("$ {file.input}") private String input; @Value ("$ {file.output}") privatni izlazni niz; @Override public void run (String ... args) baca izuzetak {JobParametersBuilder paramsBuilder = new JobParametersBuilder (); paramsBuilder.addString ("file.input", ulaz); paramsBuilder.addString ("datoteka.izlaz", izlaz); jobLauncher.run (transformBooksRecordsJob, paramsBuilder.toJobParameters ()); } // ostale metode (glavna itd.)} 

4. Testiranje proljetnog serijskog posla

The proljetno-šaržno ispitivanje ovisnost pruža skup korisnih pomoćnih metoda i slušatelja koji se mogu koristiti za konfiguriranje konteksta Spring Batch tijekom testiranja.

Stvorimo osnovnu strukturu za naš test:

@RunWith (SpringRunner.class) @SpringBatchTest @EnableAutoConfiguration @ContextConfiguration (class = {SpringBatchConfiguration.class}) @TestExecutionListeners ({DependencyInjectionTestExecutionListener.class, DirtiesContextLestCettextDextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTest ostale konstante testa @Autowired private JobLauncherTestUtils jobLauncherTestUtils; @Autowired privatni JobRepositoryTestUtils jobRepositoryTestUtils; @Nakon javne praznine cleanUp () {jobRepositoryTestUtils.removeJobExecutions (); } privatni JobParameters defaultJobParameters () {JobParametersBuilder paramsBuilder = novi JobParametersBuilder (); paramsBuilder.addString ("file.input", TEST_INPUT); paramsBuilder.addString ("datoteka.izlaz", TEST_OUTPUT); vratiti paramsBuilder.toJobParameters (); } 

The @SpringBatchTest bilješka pruža JobLauncherTestUtils i JobRepositoryTestUtils klase pomoćnika. Koristimo ih za aktiviranje Posao i Koraks u našim testovima.

Naša aplikacija koristi Automatska konfiguracija Spring Boot, koja omogućuje zadanu memoriju Spremište poslova. Kao rezultat, izvođenje više testova u istoj klasi zahtijeva korak čišćenja nakon svakog probnog rada.

Konačno, ako želimo pokrenuti više testova iz nekoliko klasa testova, svoj kontekst moramo označiti kao prljav. To je potrebno kako bi se izbjeglo sukobljavanje nekolicine Spremište poslova instance koje koriste isti izvor podataka.

4.1. Testiranje od kraja do kraja Posao

Prvo što ćemo testirati je cjeloviti end-to-end Posao s malim ulazom skupa podataka.

Zatim možemo usporediti rezultate s očekivanim izlaznim rezultatima testa:

@Test javna praznina givenReferenceOutput_whenJobExecuted_thenSuccess () baca izuzetak {// zadani FileSystemResource očekujeResult = novi FileSystemResource (EXPECTED_OUTPUT); FileSystemResource actualResult = novi FileSystemResource (TEST_OUTPUT); // kada je JobExecution jobExecution = jobLauncherTestUtils.launchJob (defaultJobParameters ()); JobInstance actualJobInstance = jobExecution.getJobInstance (); ExitStatus actualJobExitStatus = jobExecution.getExitStatus (); // zatim assertThat (actualJobInstance.getJobName (), is ("transformBooksRecords")); assertThat (actualJobExitStatus.getExitCode (), je ("ZAVRŠENO")); AssertFile.assertFileEquals (očekivani rezultat, stvarni rezultat); }

Spring Batch Test pruža korisno metoda usporedbe datoteka za provjeru rezultata pomoću AssertFile razred.

4.2. Testiranje pojedinačnih koraka

Ponekad je prilično skupo testirati komplet Posao od kraja do kraja i zato ima smisla testirati pojedinca Koraci umjesto toga:

@Test javna praznina givenReferenceOutput_whenStep1Executed_thenSuccess () baca izuzetak {// zadani FileSystemResource očekujeResult = novi FileSystemResource (EXPECTED_OUTPUT); FileSystemResource actualResult = novi FileSystemResource (TEST_OUTPUT); // kada je JobExecution jobExecution = jobLauncherTestUtils.launchStep ("step1", defaultJobParameters ()); Zbirka actualStepExecutions = jobExecution.getStepExecutions (); ExitStatus actualJobExitStatus = jobExecution.getExitStatus (); // zatim assertThat (actualStepExecutions.size (), je (1)); assertThat (actualJobExitStatus.getExitCode (), je ("ZAVRŠENO")); AssertFile.assertFileEquals (očekivani rezultat, stvarni rezultat); } @Test public void whenStep2Executed_thenSuccess () {// when JobExecution jobExecution = jobLauncherTestUtils.launchStep ("step2", defaultJobParameters ()); Zbirka actualStepExecutions = jobExecution.getStepExecutions (); ExitStatus actualExitStatus = jobExecution.getExitStatus (); // zatim assertThat (actualStepExecutions.size (), je (1)); assertThat (actualExitStatus.getExitCode (), je ("ZAVRŠENO")); actualStepExecutions.forEach (stepExecution -> {assertThat (stepExecution.getWriteCount (), je (8));}); }

Primijeti da koristimo startStep metoda za pokretanje određenih koraka.

Zapamti to dizajnirali smo i našu Predmet za čitanje i ItemWriter za upotrebu dinamičkih vrijednosti tijekom izvođenja, što znači svoje I / O parametre možemo proslijediti na Izvršenje posla(redovi 9 i 23).

Za prvu Korak test, uspoređujemo stvarni izlaz s očekivanim izlazom.

S druge strane, u drugom testu provjeravamo StepExecution za očekivane pisane stavke.

4.3. Ispitivanje komponenata s koračnim opsegom

Ajmo sada testirati FlatFileItemReader. Podsjetimo da smo to izložili kao @StepScope bean, pa ćemo za to htjeti koristiti namjensku podršku Spring Batch-a:

// prethodno automatski povezivana itemReader @Test javna praznina givenMockedStep_whenReaderCalled_thenSuccess () baca izuzetak {// zadana StepExecution stepExecution = MetaDataInstanceFactory .createStepExecution (defaultJobParameters ()); // kada StepScopeTestUtils.doInStepScope (stepExecution, () -> {BookRecord bookRecord; itemReader.open (stepExecution.getExecutionContext ()); while ((bookRecord = itemReader.read ())! = null) {// then assertThat (book .getBookName (), je ("Foundation")); assertThat (bookRecord.getBookAuthor (), is ("Asimov I.")); assertThat (bookRecord.getBookISBN (), is ("ISBN 12839")); assertThat ( bookRecord.getBookFormat (), je ("tvrdi uvez")); assertThat (bookRecord.getPublishingYear (), je ("2018"));} itemReader.close (); return null;}); }

The MetadataInstanceFactory stvara običaj StepExecution to je potrebno za ubrizgavanje našeg Step-scoped-a Predmet za čitanje.

Zbog ovoga, ponašanje čitatelja možemo provjeriti uz pomoć doInTestScope metoda.

Dalje, testirajmo JsonFileItemWriter i provjerite njegov izlaz:

@Test javna praznina givenMockedStep_whenWriterCalled_thenSuccess () baca izuzetak {// zadani FileSystemResource očekujeResult = novi FileSystemResource (EXPECTED_OUTPUT_ONE); FileSystemResource actualResult = novi FileSystemResource (TEST_OUTPUT); Book demoBook = nova knjiga (); demoBook.setAuthor ("Grisham J."); demoBook.setName ("Tvrtka"); StepExecution stepExecution = MetaDataInstanceFactory .createStepExecution (defaultJobParameters ()); // kada StepScopeTestUtils.doInStepScope (stepExecution, () -> {jsonItemWriter.open (stepExecution.getExecutionContext ()); jsonItemWriter.write (Arrays.asList (demoBook)); jsonItemWriter. null; // tada AssertFile.assertFileEquals (očekivani rezultat, stvarni rezultat); } 

Za razliku od prethodnih testova, sada imamo potpunu kontrolu nad našim ispitnim objektima. Kao rezultat, odgovorni smo za otvaranje i zatvaranje I / O tokova.

5. Zaključak

U ovom smo tutorijalu istražili različite pristupe testiranja proljetnog batch posla.

Testovima od kraja do kraja provjerava se potpuno izvršenje posla. Testiranje pojedinačnih koraka može pomoći u složenim scenarijima.

Konačno, kada je riječ o komponentama s opsegom koraka, možemo koristiti hrpu pomoćnih metoda koje pruža proljetno-šaržno ispitivanje. Pomoći će nam u sprječavanju i ismijavanju objekata domene Spring Batch.

Kao i obično, na GitHubu možemo istražiti kompletnu bazu kodova.


$config[zx-auto] not found$config[zx-overlay] not found