window.pipedriveLeadboosterConfig = { bas: 'leadbooster-chat.pipedrive.com', företagId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', version: 2, } ;(funktion () { var w = fönster if (w.LeadBooster) { console.warn('LeadBooster finns redan') } annars { w.LeadBooster = { q: [], on: funktion (n, h) { this.q.push({ t: "o", n: n, h: h }) }, trigger: funktion (n) { this.q.push({ t: 't', n: n }) }, } } })() Testbehållare - Hur gör man tester enklare? - The Codest
Codest
  • Om oss
  • Tjänster
    • Utveckling av programvara
      • Frontend-utveckling
      • Backend-utveckling
    • Staff Augmentation
      • Frontend-utvecklare
      • Backend-utvecklare
      • Dataingenjörer
      • Ingenjörer inom molntjänster
      • QA-ingenjörer
      • Övriga
    • Det rådgivande
      • Revision och rådgivning
  • Industrier
    • Fintech & bankverksamhet
    • E-commerce
    • Adtech
    • Hälsoteknik
    • Tillverkning
    • Logistik
    • Fordon
    • IOT
  • Värde för
    • VD OCH KONCERNCHEF
    • CTO
    • Leveranschef
  • Vårt team
  • Fallstudier
  • Vet hur
    • Blogg
    • Möten
    • Webbinarier
    • Resurser
Karriär Ta kontakt med oss
  • Om oss
  • Tjänster
    • Utveckling av programvara
      • Frontend-utveckling
      • Backend-utveckling
    • Staff Augmentation
      • Frontend-utvecklare
      • Backend-utvecklare
      • Dataingenjörer
      • Ingenjörer inom molntjänster
      • QA-ingenjörer
      • Övriga
    • Det rådgivande
      • Revision och rådgivning
  • Värde för
    • VD OCH KONCERNCHEF
    • CTO
    • Leveranschef
  • Vårt team
  • Fallstudier
  • Vet hur
    • Blogg
    • Möten
    • Webbinarier
    • Resurser
Karriär Ta kontakt med oss
Pil tillbaka GÅ TILLBAKA
2022-07-26
Utveckling av programvara

Testbehållare - Hur gör man tester enklare?

Bartlomiej Kuczynski

Letar du efter ett sätt att göra tester på ett enklare sätt? Vi har hittat dig! Läs följande artikel och lär dig hur du gör det möjligt.

Modern applikationsutveckling bygger på en enkel regel:

Använd sammansättning

Vi komponerar klasser, funktioner och tjänster till större delar av programvaran. Det sista elementet är grunden för mikrotjänster och hexagonal arkitektur. Vi skulle vilja använda befintliga lösningar, integrera dem med vår programvara och gå direkt till marknad.

Vill du hantera kontoregistrering och lagra användardata? Du kan välja en av OAuth-tjänsterna. Kanske erbjuder din applikation någon form av prenumeration eller betalning? Det finns många tjänster som kan hjälpa dig att hantera detta. Behöver du analyser på din webbplats, men förstår inte GDPR? Känn dig fri och ta en av de färdiga lösningarna.

Något som gör utvecklingen så enkel ur affärssynpunkt kan ge dig huvudvärk - i det ögonblick du behöver skriva ett enkelt test.

De fantastiska djuren: Köer, databaser och hur man testar dem

Enhetstestning är ganska enkelt. Om du bara följer reglerna, så kommer din testmiljö och kod är hälsosamma. Vilka regler är det?

  • Lätt att skriva - ett enhetstest ska vara lätt att skriva eftersom du skriver många av dem. Mindre ansträngning innebär att fler tester skrivs.
  • Läsbar - testkoden ska vara lättläst. Testet är en berättelse. Det beskriver programvarans beteende och kan användas som en genväg till dokumentation. Ett bra enhetstest hjälper dig att åtgärda buggar utan att felsöka koden.
  • Pålitlig - testet ska bara misslyckas om det finns en bugg i det system som testas. Självklart? Inte alltid. Ibland godkänns tester om du kör dem en och en, men misslyckas när du kör dem som en uppsättning. De godkänns på din maskin, men misslyckas på CI (Fungerar på min maskin). Ett bra enhetstest har bara en anledning till att misslyckas.
  • Snabb - Testerna ska vara snabba. Förberedelser för att köra, starta och själva testkörningen bör vara mycket snabb. Annars kommer du att skriva dem, men inte köra dem. Långsamma tester innebär förlorat fokus. Du väntar och tittar på förloppsindikatorn.
  • Oberoende - Slutligen ska testet vara oberoende. Denna regel härrör från de föregående. Endast verkligt oberoende tester kan bli en enhet. De stör inte varandra, kan köras i vilken ordning som helst och eventuella fel beror inte på resultaten från andra tester. Oberoende innebär också att det inte finns något beroende av externa resurser som databaser, meddelandetjänster eller filsystem. Om du behöver kommunicera med externa resurser kan du använda mocks, stubbar eller dummies.

Allt blir komplicerat när vi vill skriva några integrationstester. Det är inte så illa om vi vill testa några tjänster tillsammans. Men när vi behöver testa tjänster som använder externa resurser som databaser eller meddelandetjänster, då är vi ute på hal is.

För att köra testet måste du installera...

För många år sedan, när vi ville göra några integrationstester och använda t.ex. databaser, hade vi två alternativ:

  1. Vi kan installera en databas lokalt. Ställ in ett schema och anslut från vårt test;
  2. Vi kan koppla upp oss mot en befintlig instans "någonstans i rymden".

Båda hade fördelar, båda hade nackdelar. Men båda introducerar ytterligare nivåer av komplexitet. Ibland var det teknisk komplexitet som uppstod på grund av egenskaperna hos vissa verktyg, t.ex. installation och hantering av Oracle DB på ditt lokala webbhotell. Ibland var det en olägenhet i processen, t.ex. att du måste hålla med om testet Team om JMS-användning ... varje gång du vill köra tester.

Containrar till undsättning

Under de senaste 10 åren har idén om containerisering fått ett erkännande i branschen. Så ett naturligt beslut är att välja containrar som en lösning på vårt integrationstestproblem. Det här är en enkel och ren lösning. Du kör bara din process build och allt fungerar! Kan du inte tro det? Ta en titt på den här enkla konfigurationen av en maven-build:

com.dkanejs.maven.plugins
     docker-compose-maven-plugin
     4.0.0
     
       
         upp
         test-kompilera
         
           upp
          </goals
         
           ${projekt.basedir}/docker-compose.yml
           troligt
         
       
       
         ned
         test efter integration
         
           nedgång
         
         
           ${project.basedir}/docker-compose.yml
           troget
         
       
     
    </plugin
  </plugins

Och docker-compose.yml filen ser också ganska bra ut!

version: "3.5"

tjänster:

 postgres:
   container_namn: reactivedb
   image: postgres:13.2
   omstart: alltid
   miljö:
     - POSTGRES_USER=admin
     - POSTGRES_PASSWORD=password
     - POSTGRES_DB=städer
   portar:
     - "5432:5432"
   volymer:
     - postgres_data:/data/db

 pgadmin:
   behållarens_namn: pgadmin4
   avbildning: dpage/pgadmin4
   omstart: alltid
   miljö:
     PGADMIN_DEFAULT_EMAIL: [email protected]
     PGADMIN_DEFAULT_PASSWORD: lösenord
   portar:
     - "15050:80"
   volymer:
     - pgadmin_data:/data/pgadmin

volymer:
 postgres_data:
 pgadmin_data:

Men kan du se problemet här?

Ett lastfartyg som blockerar allt

Exemplet ovan är mycket enkelt. Bara en postgres-databas, pgAdmin och det är allt. När du kör

bash
$ mvn ren verifiera

sedan startar maven-plugin containrarna och stänger av dem efter testerna. Problemen börjar när projektet växer och vår compose-fil också växer. Varje gång måste du starta alla behållare, och de kommer att vara vid liv genom hela byggandet. Du kan göra situationen lite bättre genom att ändra plugin-körningskonfigurationen, men det räcker inte. I värsta fall tömmer dina containrar systemresurserna innan testerna startar!

Och detta är inte det enda problemet. Du kan inte köra ett enda integrationstest från din IDE. Innan dess måste du starta behållarna för hand. Dessutom kommer nästa maven-körning att riva ner dessa behållare (ta en titt på ner avrättning).

Så den här lösningen är som ett stort lastfartyg. Om allt fungerar bra, då är det ok. Alla oväntade eller ovanliga beteenden leder oss till någon form av katastrof.

Testcontainrar - kör containrar från tester

Men tänk om vi kunde köra våra containrar från tester? Den här idén ser bra ut, och den håller redan på att implementeras. TestbehållareEftersom vi talar om det här projektet, här är en lösning på våra problem. Den är inte idealisk, men ingen är perfekt.

Detta är en Java biblioteket, som stöder JUnit- och Spock-tester, vilket ger lätta och enkla sätt att köra Docker-containern. Låt oss ta en titt på det och skriva lite kod!

Förutsättningar och konfiguration

Innan vi börjar måste vi kontrollera vår konfiguration. Testbehållare behov:

  • Docker i version v17.09,
  • Java minst version 1.8,
  • Tillgång till nätverk, särskilt till docker.hub.

Mer information om kraven för specifika operativsystem och CI finns i
i dokumentation.

Nu är det dags att lägga till några rader till pom.xml.

Jag använder Spring Boot i projektet för att reducera boilerplate. Testbehållare är oberoende av Spring Framework och du kan använda dem utan det.
org.testcontainers
       testcontainers-bom
       ${testcontainers.version}
       pom
       importera
     
    .
  Beroendehantering
 
   
     org.postgresql
     postgresql
     körtid Beroende
   
   
     org.testcontainers
     postgresql
     test
    
   
     org.testcontainers
     junit-jupiter
     test
    </dependency
  .

Jag använder Testbehållare version 1.17.3men använd gärna den nyaste.

Tester med Postgres-behållare

Det första steget är att förbereda vår instans av en container. Du kan göra det direkt i testet, men en oberoende klass ser bättre ut.

public class Postgres13TC utökar PostgreSQLContainer {

 privat statisk slutlig Postgres13TC TC = new Postgres13TC();

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

 public static Postgres13TC getInstance() {
   return TC;
 }

 @Överstyrning
 offentligt ogiltigt start() {
   super.start();
   System.setProperty("DB_URL", TC.getJdbcUrl());
   System.setProperty("DB_USERNAME", TC.getUsername());
   System.setProperty("DB_PASSWORD", TC.getPassword());
 }

 @Överträdelse
 public void stop() {
   // gör ingenting. Detta är en delad instans. Låt JVM hantera denna operation.
 }
}

I början av testerna kommer vi att skapa en instans av Postgres13TC. Den här klassen kan hantera information om vår container. Det viktigaste här är databasens anslutningssträngar och referenser. Nu är det dags att skriva ett mycket enkelt test.

@Testbehållare
klass SimpleDbTest {

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

 @Test
 void testConnection() {
   assumeThat(postgres13TC.isRunning());
   var connectionProps = new Properties();
   connectionProps.put("user", postgres13TC.getUsername());
   connectionProps.put("password", postgres13TC.getPassword());

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

Jag använder JUnit 5 här. Annotation @Testbehållare är en del av de tillägg som styr containrar i testmiljön. De hittar alla fält med @Container annotation och start- respektive stoppbehållare.

Tester med Spring Boot

Som jag nämnde tidigare använder jag Spring Boot i projektet. I det här fallet behöver vi skriva lite mer kod. Det första steget är att skapa en ytterligare konfigurationsklass.

@Slf4j
public class ContainerInit implementerar
   ApplicationContextInitializer {

 public static Postgres13TC;

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

 @Overrid
 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()
   );
 }
}

Denna klass åsidosätter de befintliga egenskaperna med värden från testbehållare. De tre första egenskaperna är standard Spring-egenskaper. De följande fem är ytterligare, anpassade egenskaper som kan användas för att konfigurera andra resurser och tillägg som liquibase, t.ex:

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.användarnamn:admin}
spring.liquibase.password=${db.password:lösenord}
spring.liquibase.enabled=true

Nu är det dags att definiera ett enkelt integrationstest.

@SpringBootTest(webEnvironment = RANDOM_PORT)
@AutoConfigureTestDatabase(replace = NONE)
@ContextConfiguration(initialiserare = ContainerInit.class)
@Testcontainers
klass DummyRepositoryTest {

 @Autowired
 privat DummyRepository;

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

Vi har några extra anteckningar här.

  • @SpringBootTest(webEnvironment = RANDOM_PORT) - markerar testet som ett Spring Boot-test och startar Spring-kontexten.
  • @AutoConfigureTestDatabase(replace = NONE) - dessa anteckningar säger att vårtesttillägget inte ska ersätta postgres-databasens konfiguration med H2 i minneskonfigurationen.
  • @ContextConfiguration(initialiserare = ContainerInit.class) - ytterligare ett vårsammanhang
    konfiguration där vi sätter upp egenskaper från Testbehållare.
  • @Testbehållare - som tidigare nämnts styr denna annotation behållarens livscykel.

I det här exemplet använder jag reaktiva repositories, men det fungerar på samma sätt med vanliga JDBC- och JPA-repositories.

Nu kan vi köra det här testet. Om det är första gången behöver motorn hämta bilder från docker.hub. Det kan ta en stund. Efter det kommer vi att se att två containrar har körts. Den ena är postgres och den andra är Testcontainers controller. Den andra containern hanterar containrar som körs och även om JVM oväntat stannar, stänger den av containrarna och rensar upp miljön.

Låt oss sammanfatta

Testbehållare är mycket lättanvända verktyg som hjälper oss att skapa integrationstester som använder Docker-containrar. Det ger oss mer flexibilitet och ökar utvecklingshastigheten. Korrekt inställning av testkonfigurationen minskar den tid som krävs för att introducera nya utvecklare. De behöver inte konfigurera alla beroenden, utan bara köra de skrivna testerna med utvalda konfigurationsfiler.

samarbetsbanner

Relaterade artiklar

Utveckling av programvara

Bygg framtidssäkrade webbappar: Insikter från The Codest:s expertteam

Upptäck hur The Codest utmärker sig genom att skapa skalbara, interaktiva webbapplikationer med banbrytande teknik som ger sömlösa användarupplevelser på alla plattformar. Läs om hur vår expertis driver digital omvandling och affärsutveckling...

DEKODEST
Utveckling av programvara

Topp 10 Lettlandsbaserade mjukvaruutvecklingsföretag

Läs mer om Lettlands främsta mjukvaruutvecklingsföretag och deras innovativa lösningar i vår senaste artikel. Upptäck hur dessa teknikledare kan hjälpa till att lyfta ditt företag.

thecodest
Lösningar för företag och uppskalningsföretag

Java Software Development Essentials: En guide till framgångsrik outsourcing

Utforska denna viktiga guide om framgångsrik outsourcing av Java-programvaruutveckling för att förbättra effektiviteten, få tillgång till expertis och driva projektframgång med The Codest.

thecodest
Utveckling av programvara

Den ultimata guiden till outsourcing i Polen

Den kraftiga ökningen av outsourcing i Polen drivs av ekonomiska, utbildningsmässiga och tekniska framsteg, vilket främjar IT-tillväxt och ett företagsvänligt klimat.

TheCodest
Lösningar för företag och uppskalningsföretag

Den kompletta guiden till verktyg och tekniker för IT-revision

IT-revisioner säkerställer säkra, effektiva och kompatibla system. Läs mer om hur viktiga de är genom att läsa hela artikeln.

Codest
Jakub Jakubowicz CTO och medgrundare

Prenumerera på vår kunskapsbas och håll dig uppdaterad om expertisen från IT-sektorn.

    Om oss

    The Codest - Internationellt mjukvaruutvecklingsföretag med teknikhubbar i Polen.

    Förenade kungariket - Huvudkontor

    • Kontor 303B, 182-184 High Street North E6 2JA
      London, England

    Polen - Lokala tekniknav

    • Fabryczna Office Park, Aleja
      Pokoju 18, 31-564 Kraków
    • Brain Embassy, Konstruktorska
      11, 02-673 Warszawa, Polen

      Codest

    • Hem
    • Om oss
    • Tjänster
    • Fallstudier
    • Vet hur
    • Karriär
    • Ordbok

      Tjänster

    • Det rådgivande
    • Utveckling av programvara
    • Backend-utveckling
    • Frontend-utveckling
    • Staff Augmentation
    • Backend-utvecklare
    • Ingenjörer inom molntjänster
    • Dataingenjörer
    • Övriga
    • QA-ingenjörer

      Resurser

    • Fakta och myter om att samarbeta med en extern partner för mjukvaruutveckling
    • Från USA till Europa: Varför väljer amerikanska startup-företag att flytta till Europa?
    • Jämförelse av Tech Offshore Development Hubs: Tech Offshore Europa (Polen), ASEAN (Filippinerna), Eurasien (Turkiet)
    • Vilka är de största utmaningarna för CTO:er och CIO:er?
    • Codest
    • Codest
    • Codest
    • Privacy policy
    • Användarvillkor för webbplatsen

    Copyright © 2025 av The Codest. Alla rättigheter reserverade.

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