window.pipedriveLeadboosterConfig = { basis: 'leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', versie: 2, } ;(functie () { var w = venster als (w.LeadBooster) { console.warn('LeadBooster bestaat al') } anders { w.LeadBooster = { q: [], on: functie (n, h) { this.q.push({ t: 'o', n: n, h: h }) }, trigger: functie (n) { this.q.push({ t: 't', n: n }) }, } } })() Test Containers - Hoe maak je testen makkelijker? - The Codest
The Codest
  • Over ons
  • Diensten
    • Software Ontwikkeling
      • Frontend ontwikkeling
      • Backend ontwikkeling
    • Staff Augmentation
      • Frontend ontwikkelaars
      • Backend ontwikkelaars
      • Gegevensingenieurs
      • Cloud Ingenieurs
      • QA ingenieurs
      • Andere
    • Het advies
      • Audit & Consulting
  • Industrie
    • Fintech & Bankieren
    • E-commerce
    • Adtech
    • Gezondheidstechnologie
    • Productie
    • Logistiek
    • Automotive
    • IOT
  • Waarde voor
    • CEO
    • CTO
    • Leveringsmanager
  • Ons team
  • Case Studies
  • Weten hoe
    • Blog
    • Ontmoetingen
    • Webinars
    • Bronnen
Carrière Neem contact op
  • Over ons
  • Diensten
    • Software Ontwikkeling
      • Frontend ontwikkeling
      • Backend ontwikkeling
    • Staff Augmentation
      • Frontend ontwikkelaars
      • Backend ontwikkelaars
      • Gegevensingenieurs
      • Cloud Ingenieurs
      • QA ingenieurs
      • Andere
    • Het advies
      • Audit & Consulting
  • Waarde voor
    • CEO
    • CTO
    • Leveringsmanager
  • Ons team
  • Case Studies
  • Weten hoe
    • Blog
    • Ontmoetingen
    • Webinars
    • Bronnen
Carrière Neem contact op
Pijl terug KEREN TERUG
2022-07-26
Software Ontwikkeling

Testcontainers - Hoe maak je testen eenvoudiger?

Bartlomiej Kuczynski

Ben je op zoek naar een manier om eenvoudiger testen te maken? We hebben je! Bekijk het volgende artikel en leer hoe je dit mogelijk maakt.

Moderne applicatieontwikkeling is gebaseerd op één eenvoudige regel:

Gebruik samenstelling

We voegen klassen, functies en services samen tot grotere stukken software. Dat laatste element is de basis van microservices en zeshoekige architectuur. We willen graag bestaande oplossingen gebruiken, deze integreren met onze software en rechtstreeks naar de markt.

Wil je accountregistratie afhandelen en gebruikersgegevens opslaan? Dan kun je een van de OAuth-services kiezen. Misschien biedt je applicatie een soort abonnement of betaling aan? Er zijn veel diensten die je hierbij kunnen helpen. Heb je analytics nodig op je website, maar begrijp je GDPR niet? Neem gerust een van de kant-en-klare oplossingen.

Iets dat ontwikkeling vanuit zakelijk oogpunt zo eenvoudig maakt, kan je hoofdpijn bezorgen - het moment waarop je een eenvoudige test moet schrijven.

De Fantastic Beasts: Wachtrijen, databases en hoe ze te testen

Unit testen is vrij eenvoudig. Als je alleen de regels volgt, dan zullen je testomgeving en code gezond zijn. Welke regels zijn dat?

  • Gemakkelijk te schrijven - Een unit test moet gemakkelijk te schrijven zijn omdat je er veel van schrijft. Minder moeite betekent dat er meer tests worden geschreven.
  • Leesbaar - De testcode moet gemakkelijk te lezen zijn. De test is een verhaal. Het beschrijft het gedrag van software en kan worden gebruikt als een snelkoppeling voor documentatie. Een goede unit test helpt je om bugs op te lossen zonder de code te debuggen.
  • Betrouwbaar - de test mag alleen mislukken als er een fout zit in het systeem dat getest wordt. Duidelijk? Niet altijd. Soms slagen tests als je ze één voor één uitvoert, maar mislukken ze als je ze als een set uitvoert. Ze slagen op jouw machine, maar falen op CI (Werkt op mijn machine). Een goede unit test heeft slechts één reden voor mislukking.
  • Snel - testen moeten snel zijn. De voorbereiding, de start en de testuitvoering zelf moeten heel snel zijn. Anders schrijf je ze wel, maar voer je ze niet uit. Trage tests betekenen verlies van focus. Je wacht en kijkt naar de voortgangsbalk.
  • Onafhankelijk - Ten slotte moet de test onafhankelijk zijn. Deze regel komt voort uit de vorige. Alleen echt onafhankelijke tests kunnen een eenheid worden. Ze interfereren niet met elkaar, kunnen in elke volgorde worden uitgevoerd en potentiële fouten zijn niet afhankelijk van de resultaten van andere tests. Onafhankelijk betekent ook geen afhankelijkheid van externe bronnen zoals databases, berichtendiensten of bestandssystemen. Als je moet communiceren met externen, kun je mocks, stubs of dummy's gebruiken.

Alles wordt ingewikkeld als we enkele integratietests willen schrijven. Het is niet erg als we een paar services samen willen testen. Maar als we services moeten testen die externe bronnen gebruiken, zoals databases of berichtendiensten, dan vragen we om problemen.

Om de test uit te voeren, moet je...

Vele jaren geleden, toen we enkele integratietests wilden maken en bijvoorbeeld databases wilden gebruiken, hadden we twee opties:

  1. We kunnen lokaal een database installeren. Stel een schema in en maak verbinding vanuit onze test;
  2. We kunnen verbinding maken met een bestaande instantie "ergens in de ruimte".

Beide hadden voor- en nadelen. Maar beide introduceren extra niveaus van complexiteit. Soms was het technische complexiteit die voortkwam uit de eigenschappen van bepaalde tools, bijvoorbeeld de installatie en het beheer van Oracle DB op je localhost. Soms was het een ongemak in het proces, bijv. je moet het eens zijn met de test team over JMS-gebruik... elke keer dat je tests wilt uitvoeren.

Containers als redding

In de afgelopen 10 jaar heeft het idee van containerisatie steeds meer erkenning gekregen in de industrie. Het is dus een logische keuze om containers te kiezen als oplossing voor ons integratietestprobleem. Dit is een eenvoudige, schone oplossing. Je hoeft alleen maar je proces build uit te voeren en alles werkt! Kun je het niet geloven? Kijk dan eens naar deze eenvoudige configuratie van een maven build:

com.dkanejs.maven.plugins/groupId>
     docker-compose-maven-plugin
     4.0.0
     
       
         op
         test-compileren
         
           op
         
         
           ${project.basedir}/docker-compose.yml
           true
         
       
       
         down
         post-integratie-test
         
           down
         
         
           ${project.basedir}/docker-compose.yml
           true

En de docker-compose.yml Het bestand ziet er ook mooi uit!

versie: "3.5"

diensten:

 postgres:
   container_naam: reactivedb
   image: postgres:13.2
   herstart: altijd
   omgeving:
     - POSTGRES_GEBRUIKER=admin
     - POSTGRES_WACHTWOORD=wachtwoord
     - POSTGRES_DB=steden
   poorten:
     - "5432:5432"
   volumes:
     - postgres_data:/data/db

 pgadmin:
   container_naam: pgadmin4
   image: dpage/pgadmin4
   herstart: altijd
   omgeving:
     PGADMIN_DEFAULT_EMAIL: [email protected]
     PGADMIN_DEFAULT_PASSWORD: wachtwoord
   poorten:
     - "15050:80"
   volumes:
     - pgadmin_data:/data/pgadmin

volumes:
 postgres_data:
 pgadmin_data:

Maar kun je het probleem hier zien?

Een vrachtschip dat alles blokkeert

Het bovenstaande voorbeeld is heel eenvoudig. Gewoon één postgres database, pgAdmin en dat is alles. Wanneer je

bash
$ mvn clean verifiëren

dan start de maven-plugin de containers en schakelt ze na de tests weer uit. Er ontstaan problemen als het project groeit en ons composeerbestand ook. Elke keer moet je alle containers starten en ze zullen de hele build in leven blijven. Je kunt de situatie een beetje verbeteren door de uitvoeringsconfiguratie van de plugin te veranderen, maar dat is niet genoeg. In het ergste geval putten je containers systeembronnen uit voordat de tests starten!

En dit is niet het enige probleem. Je kunt geen enkele integratietest uitvoeren vanuit je IDE. Daarvoor moet je de containers met de hand starten. Bovendien zal de volgende maven run die containers afbreken (kijk eens naar naar beneden uitvoering).

Deze oplossing is dus als een groot vrachtschip. Als alles goed werkt, dan is het ok. Elk onverwacht of ongewoon gedrag leidt ons naar een soort ramp.

Testcontainers - containers uitvoeren vanuit tests

Maar wat als we onze containers konden uitvoeren vanuit tests? Dit idee ziet er goed uit en wordt al geïmplementeerd. TestcontainersOmdat we het over dit project hebben, is hier een oplossing voor onze problemen. Niet ideaal, maar niemand is perfect.

Dit is een Java bibliotheek, die JUnit- en Spock-tests ondersteunt en lichtgewicht en eenvoudige manieren biedt om de Docker-container uit te voeren. Laten we er eens naar kijken en wat code schrijven!

Vereisten en configuratie

Voordat we beginnen, moeten we onze configuratie controleren. Testvaten nodig hebben:

  • Docker in versie v17.09,
  • Java minimaal versie 1.8,
  • Toegang tot het netwerk, vooral tot docker.hub.

Meer over de vereisten voor specifieke OS en CI kun je vinden
in documentatie.

Nu is het tijd om wat regels toe te voegen aan pom.xml.

Ik gebruik spring boot in het project om boilerplate te verminderen. Testvaten zijn onafhankelijk van Spring Framework en je kunt ze zonder dat gebruiken.
org.testcontainers/groupId>
       testcontainers-bom
       ${testcontaines.version}
       pom
       import
     
   
 
 
   
     org.postgresql
     postgresql
     runtime
   
   
     org.testcontainers/groupId>
     postgresql
     test
   
   
     org.testcontainers/groupId>
     junit-jupiter
     test

Ik gebruik Testvaten versie 1.17.3maar voel je vrij om de nieuwste te gebruiken.

Tests met Postgres-container

De eerste stap is het voorbereiden van onze instantie van een container. Je kunt dat direct in de test doen, maar een onafhankelijke klasse ziet er beter uit.

openbare klasse Postgres13TC uitbreidt PostgreSQLContainer {

 private static final Postgres13TC TC = nieuwe Postgres13TC();

 private Postgres13TC() {
   super("postgres:13.2");
 }

 public static Postgres13TC getInstance() {
   TC terug;
 }

 @Override
 public void start() {
   super.start();
   System.setProperty("DB_URL", TC.getJdbcUrl());
   System.setProperty("DB_USERNAME", TC.getUsername());
   System.setProperty("DB_PASSWORD", TC.getPassword());
 }

 @Override
 openbare void stop() {
   // niets doen. Dit is een gedeelde instantie. Laat JVM deze operatie afhandelen.
 }
}

Aan het begin van de tests maken we een instantie van Postgres13TC. Deze klasse kan informatie over onze container verwerken. Het belangrijkste hier zijn de database connectie strings en credentials. Nu is het tijd om een heel eenvoudige test te schrijven.

@Testcontainers
klasse SimpleDbTest {

 @Container
 private statische Postgres13TC = Postgres13TC.getInstance();

 @Test
 id testConnection() {
   assumeThat(postgres13TC.isRunning());
   var connectionProps = nieuwe Properties();
   connectionProps.put("user", postgres13TC.getUsername());
   connectionProps.put("wachtwoord", postgres13TC.getPassword());

   try (Connection = DriverManager.getConnection(postgres13TC.getJdbcUrl(),
       connectionProps)) {
     var resultSet = connection.prepareStatement("Select 1").executeQuery();
     resultSet.next();
     assertThat(resultSet.getInt(1)).isEqualTo(1);
   } vang (SQLException sqlException) {
     assertThat((Exception) sqlException).doesNotThrowAnyException();
   }
 }
}

Ik gebruik hier JUnit 5. Annotatie @Testcontainers is een onderdeel van de extensies die containers in de testomgeving controleren. Ze vinden alle velden met @Container annotatie en respectievelijk start- en stopcontainers.

Testen met Spring Boot

Zoals ik al eerder zei, gebruik ik Spring Boot in het project. In dit geval moeten we iets meer code schrijven. De eerste stap is het maken van een extra configuratieklasse.

@Slf4j
openbare klasse ContainerInit implementeert
   ApplicationContextInitializer {

 public statische Postgres13TC;

 static {
   postgres13TC = Postgres13TC.getInstance();
   postgres13TC.start();
 }

 @Override
 public void initialize(ConfigurableApplicationContext applicationContext) {
   TestPropertySourceUtils.addInlinedPropertiesToEnvironment(
       applicationContext,
       "spring.datasource.url=" + postgres13TC.getJdbcUrl(),
       "spring.datasource.username=" + postgres13TC.getUsername(),
       "spring.datasource.password=" + postgres13TC.getPassword(),
       "db.host=" + postgres13TC.getHost(),
       "db.port=" + postgres13TC.getMappedPort(postgres13TC.POSTGRESQL_PORT),
       "db.name=" + postgres13TC.getDatabaseName(),
       "db.username=" + postgres13TC.getUsername(),
       "db.password=" + postgres13TC.getPassword()
   );
 }
}

Deze klasse overschrijft de bestaande eigenschappen met waarden uit de testcontainer. De eerste drie eigenschappen zijn standaard Spring eigenschappen. De volgende vijf zijn aanvullende, aangepaste eigenschappen die kunnen worden gebruikt om andere bronnen en extensies zoals liquibase te configureren, bijv:

spring.liquibase.change-log=classpath:/db/changelog/dbchangelog.xml
spring.liquibase.url=jdbc:postgresql://${db.host:localhost}:${db.port:5432}/${db.name:cities}
spring.liquibase.user=${db.username:admin}
spring.liquibase.password=${db.password:wachtwoord}
spring.liquibase.enabled=true

Nu is het tijd om een eenvoudige integratietest te definiëren.

@SpringBootTest(webomgeving = RANDOM_PORT)
@AutoConfigureTestDatabase(vervangen = GEEN)
@ContextConfiguratie(initializers = ContainerInit.class)
@Testcontainers
klasse DummyRepositoryTest {

 @Autowired
 private DummyRepository;

 @Test
 id shouldReturnDummy() {
   var byId = dummyRepository.getById(10L);
   var expected = nieuwe Dummy();
   expected.setId(10L);
   assertThat(byId).completes().emitsCount(1).emits(expected);
 }
}

We hebben hier wat extra annotaties.

  • @SpringBootTest(webomgeving = RANDOM_PORT) - markeert de test als een Spring Boot test en start de Spring context.
  • @AutoConfigureTestDatabase(vervangen = GEEN) - deze annotaties zeggen dat de springtestuitbreiding de postgres databaseconfiguratie niet mag vervangen door H2 in de geheugenconfiguratie.
  • @ContextConfiguratie(initializers = ContainerInit.class) - een extra veercontext
    configuratie waar we eigenschappen instellen van Testvaten.
  • @Testcontainers - Zoals eerder vermeld, regelt deze annotatie de levenscyclus van de container.

In dit voorbeeld gebruik ik reactive repositories, maar het werkt hetzelfde met gewone JDBC en JPA repositories.

Nu kunnen we deze test uitvoeren. Als het de eerste keer is, moet de engine images ophalen van docker.hub. Dat kan even duren. Daarna zullen we zien dat er twee containers draaien. De ene is postgres en de andere is Testcontainers controller. Die tweede container beheert de draaiende containers en zelfs als JVM onverwacht stopt, schakelt het de containers uit en ruimt het de omgeving op.

Laten we samenvatten

Testvaten zijn zeer eenvoudig te gebruiken tools die ons helpen om integratietests te maken die Docker-containers gebruiken. Dat geeft ons meer flexibiliteit en verhoogt de ontwikkelingssnelheid. Het goed instellen van testconfiguratie vermindert de tijd die nodig is voor nieuwe ontwikkelaars. Ze hoeven niet alle afhankelijkheden in te stellen, maar alleen de geschreven tests uit te voeren met geselecteerde configuratiebestanden.

vaandel samenwerking

Verwante artikelen

Software Ontwikkeling

Bouw Toekomstbestendige Web Apps: Inzichten van The Codest's Expert Team

Ontdek hoe The Codest uitblinkt in het creëren van schaalbare, interactieve webapplicaties met geavanceerde technologieën, het leveren van naadloze gebruikerservaringen op alle platforms. Ontdek hoe onze expertise digitale transformatie en business...

DE BESTE
Software Ontwikkeling

Top 10 in Letland gevestigde bedrijven voor softwareontwikkeling

Lees meer over de beste softwareontwikkelingsbedrijven van Letland en hun innovatieve oplossingen in ons nieuwste artikel. Ontdek hoe deze technologieleiders uw bedrijf kunnen helpen verbeteren.

thecodest
Oplossingen voor ondernemingen en schaalvergroting

Essentiële Java-softwareontwikkeling: Een gids voor succesvol uitbesteden

Verken deze essentiële gids over succesvolle outsourcing Java-softwareontwikkeling om de efficiëntie te verbeteren, toegang te krijgen tot expertise en projectsucces te stimuleren met The Codest.

thecodest
Software Ontwikkeling

De ultieme gids voor outsourcing in Polen

De sterke groei van outsourcing in Polen wordt gedreven door economische, educatieve en technologische vooruitgang, die IT-groei en een bedrijfsvriendelijk klimaat stimuleert.

DeCodest
Oplossingen voor ondernemingen en schaalvergroting

De complete gids voor IT-auditmiddelen en -technieken

IT-audits zorgen voor veilige, efficiënte en compliant systemen. Lees het volledige artikel om meer te weten te komen over het belang ervan.

The Codest
Jakub Jakubowicz CTO & medeoprichter

Abonneer je op onze kennisbank en blijf op de hoogte van de expertise uit de IT-sector.

    Over ons

    The Codest - Internationaal softwareontwikkelingsbedrijf met technische hubs in Polen.

    Verenigd Koninkrijk - Hoofdkantoor

    • Kantoor 303B, 182-184 High Street North E6 2JA
      Londen, Engeland

    Polen - Lokale technologieknooppunten

    • Fabryczna kantorenpark, Aleja
      Pokoju 18, 31-564 Krakau
    • Hersenambassade, Konstruktorska
      11, 02-673 Warschau, Polen

      The Codest

    • Home
    • Over ons
    • Diensten
    • Case Studies
    • Weten hoe
    • Carrière
    • Woordenboek

      Diensten

    • Het advies
    • Software Ontwikkeling
    • Backend ontwikkeling
    • Frontend ontwikkeling
    • Staff Augmentation
    • Backend ontwikkelaars
    • Cloud Ingenieurs
    • Gegevensingenieurs
    • Andere
    • QA ingenieurs

      Bronnen

    • Feiten en fabels over samenwerken met een externe partner voor softwareontwikkeling
    • Van de VS naar Europa: Waarom Amerikaanse startups besluiten naar Europa te verhuizen
    • Tech Offshore Ontwikkelingshubs Vergelijking: Tech Offshore Europa (Polen), ASEAN (Filippijnen), Eurazië (Turkije)
    • Wat zijn de grootste uitdagingen voor CTO's en CIO's?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Gebruiksvoorwaarden website

    Copyright © 2025 door The Codest. Alle rechten voorbehouden.

    nl_NLDutch
    en_USEnglish de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench pl_PLPolish arArabic it_ITItalian jaJapanese ko_KRKorean es_ESSpanish etEstonian elGreek nl_NLDutch