Voici BDD appliqué à Spring Batch, le batch d’extraction sur Github springbatch-sample sert de support.
Cucumber-JVM a permis d’exécuter et d’implémenter très simplement les étapes correspondantes à des scénarios décrits dans le formalisme BDD.
Etant donnée les 2 scénarios d’extraction en langage Gherkin
| Feature: batch extraction+ | |
| Scenario: scenario 1 | |
| Given les utilisateurs | |
| |Id|Nom|Prenom| | |
| |1|nom1|prenom1| | |
| |2|nom2|prenom2| | |
| When je charge les utilisateurs en base de données | |
| And j'execute le job d'extraction | |
| Then mon fichier de sortie contient les lignes | |
| |1,prenom1,nom1| | |
| |2,prenom2,nom2| | |
| Scenario: scenario 2 | |
| Given les utilisateurs | |
| |Id|Nom|Prenom| | |
| |1|nom1|prenom1| | |
| When je charge les utilisateurs en base de données | |
| And j'execute le job d'extraction | |
| Then mon fichier de sortie contient les lignes | |
| |1,prenom1,nom1| |
On configure le runner Junit
| package com.giovanetti.sample.batch.job.cucumber; | |
| import com.giovanetti.support.batch.rule.BatchProperties; | |
| import cucumber.api.CucumberOptions; | |
| import cucumber.api.junit.Cucumber; | |
| import org.junit.BeforeClass; | |
| import org.junit.runner.RunWith; | |
| @RunWith(Cucumber.class) | |
| @CucumberOptions(format = {"pretty", "html:target/cucumber-report"}, | |
| glue = "com.giovanetti.sample.batch.job.cucumber") | |
| public class ExtractionFeatureTest { | |
| @BeforeClass | |
| public static void setupClass() { | |
| BatchProperties.getDefault().create(); | |
| } | |
| } |
Et on implémente les étapes
Dbsetup permet d’insérer directement les données des scénarios en base de données.
| package com.giovanetti.sample.batch.job.cucumber; | |
| import com.giovanetti.sample.batch.configuration.JobExtractionTestConfiguration; | |
| import com.giovanetti.sample.batch.item.User; | |
| import com.giovanetti.sample.batch.job.JobExtractionConfiguration; | |
| import com.giovanetti.support.batch.annotations.FunctionalDataSource; | |
| import com.ninja_squad.dbsetup.DbSetup; | |
| import com.ninja_squad.dbsetup.destination.DataSourceDestination; | |
| import com.ninja_squad.dbsetup.operation.Insert; | |
| import cucumber.api.DataTable; | |
| import cucumber.api.java.After; | |
| import cucumber.api.java.Before; | |
| import cucumber.api.java.en.Given; | |
| import cucumber.api.java.en.Then; | |
| import cucumber.api.java.en.When; | |
| import org.apache.commons.io.FileUtils; | |
| import org.springframework.batch.core.JobParametersBuilder; | |
| import org.springframework.batch.test.JobLauncherTestUtils; | |
| import org.springframework.test.annotation.DirtiesContext; | |
| import org.springframework.test.context.ContextConfiguration; | |
| import javax.inject.Inject; | |
| import javax.sql.DataSource; | |
| import java.io.File; | |
| import java.io.IOException; | |
| import java.nio.file.Files; | |
| import java.util.List; | |
| import static com.ninja_squad.dbsetup.Operations.insertInto; | |
| import static org.assertj.core.api.Assertions.assertThat; | |
| @ContextConfiguration(classes = {JobExtractionTestConfiguration.class, JobLauncherTestUtils.class}) | |
| @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) | |
| public class ExtractionSteps { | |
| private static final String USER_TABLE = "USER"; | |
| private static final String[] USER_COLUMNS = new String[]{"ID", "NOM", "PRENOM"}; | |
| private File outputFile; | |
| private DataSourceDestination destination; | |
| @Inject | |
| @FunctionalDataSource | |
| private void setDataSourceDestination(DataSource dataSource) { | |
| destination = new DataSourceDestination((dataSource)); | |
| } | |
| @Inject | |
| private JobLauncherTestUtils jobLauncherTestUtils; | |
| private List<User> users; | |
| @Before | |
| public void createOutputFile() throws IOException { | |
| outputFile = createTempFile(); | |
| } | |
| private File createTempFile() throws IOException { | |
| File tempFolder = File.createTempFile("junit", ""); | |
| if (tempFolder.delete() && tempFolder.mkdir()) { | |
| return File.createTempFile("junit", null, tempFolder); | |
| } else { | |
| throw new IllegalStateException("createTempFile fail"); | |
| } | |
| } | |
| @After | |
| public void deleteOutputFile() throws IOException { | |
| FileUtils.deleteDirectory(outputFile.getParentFile()); | |
| } | |
| @Given("^les utilisateurs$") | |
| public void les_utilisateurs(DataTable table) { | |
| users = table.asList(User.class); | |
| } | |
| @When("^je charge les utilisateurs en base de données$") | |
| public void je_charge_les_utilisateurs_en_base_de_données() { | |
| Insert.Builder insertBuilder = insertInto(USER_TABLE).columns(USER_COLUMNS); | |
| users.forEach(user -> insertBuilder.values(user.getId(), user.getNom(), user.getPrenom())); | |
| new DbSetup(destination, insertBuilder.build()).launch(); | |
| } | |
| @When("^j'execute le job d'extraction$") | |
| public void j_execute_le_job_d_extraction() throws Exception { | |
| jobLauncherTestUtils.launchJob( | |
| new JobParametersBuilder().addString(JobExtractionConfiguration.OUTPUT_FILE_PARAMETER, | |
| outputFile.getPath()).toJobParameters()); | |
| } | |
| @Then("^mon fichier de sortie contient les lignes$") | |
| public void mon_fichier_de_sortie_contient_les_lignes(List<String> lines) throws IOException { | |
| assertThat(Files.readAllLines(outputFile.toPath())).hasSize(lines.size()).containsAll(lines); | |
| } | |
| } |
Concernant l’alternative JBehave en tant que framework BDD à la place de Cucumber, la mise en place s’est avérée moins rapide et la configuration plus verbeuse, un exemple est aussi disponible dans springbatch-sample
