Effektiv avhengighetsinjeksjon i Apex (2023)

Et vanlig problem ved programmering er hvordan du holder koden din ordentlig organisert – både for å hjelpe til med lesbarhet, redusere mengden testoppsett som er nødvendig, og øke hastigheten på din evne til å levere ny kode ved å gjøre riktig gjenbruk av eksisterende kode. For alle disse tingene kan Dependency injection (DI) være en verdifull teknikk for å lære og bruke på koden du skriver. Riktig brukt, gjør DI testing av de forretningskritiske kodebanene dine enkelt og morsomt, og koden din fokusert og gjenbrukbar. I dette innlegget vil vi gjennomgå tre grunnleggende tilnærminger til avhengighetsinjeksjon; vi skal også se på hvordan du best kan bruke avhengighetsinjeksjon på både eksisterende og greenfield-prosjekter.

Grunnleggende om avhengighetsinjeksjon

Når det gjelder avhengighetsinjeksjon innen datavitenskap, kan det være nyttig å starte med 20 000 fot-visningen: hvis programmet ditt består av objekter, så er avhengighetsinjeksjon mekanismen dingjenstanderer sammensatt. Hvorfor betyr det noe? For våre spesielle formål er en grunn til at det er viktig å gjøre med Salesforces kodedekningskrav; i stedet for å skriveflere tester, kan vi optimalisere koden vår ved å sette lignende funksjonalitet i entallsklasser. På denne måten kan vi både redusere mengden kode vi trenger å skriveoghvor mye kode vi må teste.

Når du har startet skikkeliginnkapslingkoden din i separate klasser (partisjonering av logikk i objekter, eller klasser, som er et av de viktigste konseptene i objektorientert programmering), er du nå på god vei mot å kunne dra nytte av avhengighetsinjeksjon. La oss se på tre forskjellige mulige avhengighetsinjeksjonsløsninger i Apex:

Konstruktørbasert avhengighetsinjeksjon

Dette er det mest brukte DI-alternativet. Siden utviklere i flere tiår har blitt oppfordret til å liste konstruktører på toppen av klassene sine (etter "avis"-stilen, eller ovenfra-og-ned-tilnærming til lesbarhet), la oss en konstruktørbasert avhengighetsinjeksjon si hva klassen vår vil gjøre basert på klassene. gikk inn i det:

Noen som leser denne klassen for første gang drar nytte av at avhengighetene har vært en del av objektet som blir konstruert; denne klassen har en slank liste over ansvarsområder, og kan enkelt testes. Det er lett å fortsette denne veien. Mens en klasse ringteOpportunityUpdaterkan være fornuftig i noen tilfeller, kan det være enda mer fornuftig for deg å ha et objekt assosiert medDML, somOpportunityUpdaterkan da ta inn:

Merk at disse metodene ikke er statiske - de er instansmetoder. Selv om det kan være nyttig å skille mellom statiske og instansmetoder ved å se på om en metode samhandler med noen medlemsvariabler (hvis den ikke gjør det, kan den gjøres statisk), forteller det ikke hele historien. Hvis vi vil sende en klasse rundt (ved å bruke avhengighetsinjeksjon) for å kapsle inn domenespesifikk atferd, er det nødvendig at metodene er ikke-statiske. Klasser som får bestått en avhengighet (kalle klasser, noen ganger referert til som "klient"-klasser) kan ikke kalle statiske metoder på den spesifikke forekomsten av klassen som de har blitt bestått - de kan bare kalle globale eller offentlige forekomstmetoder.

OpportunityUpdaterser litt annerledes ut:

Pålydende kan det virke som om vi har gått for langt. Mens vårOpportunityUpdaterdelegerer nå selveDatabase.oppdateringoperasjon tilDML, vi harla tilen ekstra avhengighet til konstruktøren vår! Kommer dette virkelig til å gagne oss i det lange løp?

Hvis vi lot ting stå som de sto, kanskje ikke. Den sanne kraften til avhengighetsinjeksjon skyldes bare delvis at objekter er riktig innkapslet; økningen i testbarhet vi får som et resultat er den virkelige drivkraften. I klassisk objektorientert programmering,DMLrepresenterer en viktiggrensei systemet; det er øyeblikket vi leverer SObjects til plattformen. Med andre ord - det er det rette stedet å introdusere hån for å redusere testavhengighetene våre. Mens du med standard Salesforce-kode kan bli tvunget til å sette inn / oppdatere / slette / angre sletting av poster etter behov for å oppfylle ulike forretningskrav, med bare litt ekstra arbeid, vår nyeDMLobjekt kan redusere testoppsettet og seremonien som kreves for å teste forretningslogikken vår fullt ut:

Merk at for at vi skal kunne lage metoder som kan overstyres, er det også nødvendig at klassen vår merkes somvirtuell. Brukervirtuellsøkeord er perfekt når du har en standardimplementering i tankene, men ønsker å gi alternativer for hvordan individuelle metoder kan tilpasses. Kontrast dette medabstraktnøkkelord, som hindrer en klasse fra å bli spesifikt initialisert, men tillater gjenbruk av kode innenfor underklasser. Å bruke grensesnitt for en klasse som dette vil bli betraktet som et antimønster: når du bare har én konkret forekomst av en klasse, å ha et grensesnitt for å representere den offentlige API-en for en klasse somDMLer en unødvendig abstraksjon. På den annen side, hvis du skrev denne koden med et øye for emballasje, hvorDMLrepresenterte den tiltenkte grunnleggende funksjonaliteten, men det kan være mange forskjellige implementeringer, ved å bruke et grensesnitt somIDMLÅ representere den offentlige API-en for denne klassen ville være et fornuftig trekk.

Nå, når vi går for å testeOpportunityUpdater, vi trenger bare en liten hånDMLobjekt for å fjerne Salesforce-databasen fullstendig fra testligningen vår:

Som nevnt i kommentaren ovenfor, i en ekte kodebase,DMLMockville være sin egen frittstående klasse - og den ville bli brukt i hele kodebasen for å validere enkeltobjekt DML-setninger uten å måtte stole på databasen. Merk at dette ikke fritar oss fra ansvaret for å ha ekte integrasjonstester med faktisk kryssobjekt DML.

Eiendomsbasert avhengighetsinjeksjon

Vi kan også bruke offentlige egenskaper for å angi våre avhengigheter:

I dette eksemplet, merk at vi ikke lenger passerer vårDMLeksempel i gjennomOpportunityUpdatersin konstruktør. I stedet er den offentlig eksponert og kan stilles inn tilsvarende når som helst før den brukes første gang i en klasse. Merk at dette bryter innkapslingen; nå objekter utenforOpportunityUpdatervet to ting:

  1. Det er en offentlig eiendom kalt "DML" påOpportunityUpdater, og den har en typeDML
  2. DeDMLtype har metoder på seg somdoUpdate

Dette innkapslingsbruddet er grunnen til at jeg vanligvis ikke anbefaler eiendomsbasert avhengighetsinjeksjon. Eiendomsbaserte gettere og settere har sine brukstilfeller. De er fantastiske for dovent initialisering av egenskaper som ikke er tilgjengelige via alle kodebaner på et objekt, og de er selvfølgelig flotte når de forbereder dataoverføringsobjekter (DTOer), men for avhengighetsinjeksjon tar de en sidelinje til det konstruktørbaserte nærme seg.

@TestVisible avhengighetsinjeksjon

Salesforce avslører@testVisibledekoratør for egenskaper og klassedefinisjoner; dette representerer et ekstra verktøy i vårt arsenal når vi vurderer måter å injisere egenskaper i klasser. Derimot,@testVisibleegenskaper bør brukes med forsiktighet, av en rekke årsaker:

Ved hjelp av@testVisibleå utføre avhengighetsinjeksjon har en tendens til å føre til en ujevn balanse når det kommer til innkapsling. På den ene siden vet du helt sikkert at din støttedmleiendom kan bare byttes ut i tester, noe som er flott. På den andre introduserer den et avvik mellom konstruktøren og testene. Hvis det over tid blir gjort ytterligere oppsett pådmleiendom (eller hvilken som helst@testVisibleeiendom som blir hånet i tester), blir det mulig for disse små avvikene mellom produksjonsnivå og testkode å introdusere feil.

Når det gjelder DRY (eller "ikke gjenta deg selv" - et av ordtakene innen programvareutvikling som vi prøver å følge så mye som mulig), kan du forenkle testoppsettet ditt ved å lage private statiske hjelpemetoder som tar vare på å sette inndmleiendom før du returnerer den initialiserteOpportunityUpdateri tester.

Den legger også til enytterligereansvar forOpportunityUpdater(og alle andre lignende objekter). Nå, denOpportunityUpdatertrenger å vite om den spesifikke typenDMLklasse som den skal instansiere i tilfelle den falske støttevariabelen ikke er angitt. Selv om dette kanskje ikke direkte bryter innkapslingen, er det det vi vil kalle en "kodelukt" - noen få steder kan det være greit, men jo mer av dette mønsteret vi ser, jo vanskeligere er det å opprettholde.

Ett område hvor@testVisibleDI fungerer bra er innenfor Apex-biblioteker. Hvis du bare har én konkret implementering for en klasse/metode i biblioteket ditt, men du må spotte det internt som en avhengighet i dine egne tester, er dette et område hvor@testVisibleføles som et OK kompromiss. Du bryter ikke innkapslingen til omverdenen som biblioteksforfatter, men du kan fortsatt skrive lynraske enhetstester.

Bruker avhengighetsinjeksjon for å rydde opp i eksisterende kode

Når du jobber med eksisterende kode, se etter repeterende kode som kan trekkes ut til sine egne kode-"enheter" ved å lage nye klasser for å innkapsle den tidligere dupliserte oppførselen. Bruk avhengighetsinjeksjon for å legge til de nylig innkapslede objektene tilbake i klassen(e) der oppførselen gjentatte ganger ble duplisert. Bruk denne metoden for å stimulere til inkrementelle forbedringer; la koden være renere enn da du fant den, og over tid vil den eksisterende kvaliteten på kodebasen naturligvis forbedres.

Hvis koden du trekker ut er dårlig testet, bruk dette som en mulighet til å øke testdekningen ved å skrive tester; utfør refaktoriseringen først når du er fornøyd med din eksisterende kodedekning. Dette gir deg et sikkerhetsnett når det er på tide å validere at endringene du gjør for å konsolidere eksisterende kode, ikke har ødelagt noe. Hvis alle testene består, kan du vurdere å skrive objektspesifikk testdekning og flytte noe av testbyrden bort fra området der DI oppstår til det nyopprettede objektet du bruker DI på.

Som en generell tommelfingerregel, hvis du setter inn mer enn tre «lag» med Salesforce-poster (la oss si Oppgave → Kontakt → Konto) i oppsettet for testene dine, er det sannsynlig at objektet som testes gjør for mye og at det kan dra nytte av å motta avhengigheter som tar seg av noen av dets ansvar.

Hvordan designe et greenfield-prosjekt riktig for å bruke avhengighetsinjeksjon

I et helt nytt prosjekt er det mange viktige beslutninger å ta, for eksempel hva delingsmodellen din vil være og hva slags triggerrammeverk du ender opp med å bruke. Når du designer systemet ditt, kan avhengighetsinjeksjon være et veldig sterkt verktøy i arsenalet ditt hvis du husker å sette inn riktiggrensermellom de forskjellige områdene av systemet ditt.

Som et eksempel kan riktig innkapsling av objekter føre til flere distinkte domener:

  • Forespørsler
  • DML
  • API-kall
  • Trigger management
  • Applikasjonsspesifikk atferd (som konvertering av potensielle kunder, saksbehandling osv.)

Fokuser på enhetstester for å dekke nøkkelfunksjonalitet med ende-til-ende-tester når det er nødvendig (spesielt for kryssobjektbaserte oppdateringer). Hvis det gjøres riktig, betyr dette at hoveddelen av den triggerbaserte koden din vil være integrasjonstester med ekte DML som utføres. På samme måte med planlagte klasser. DI kan hjelpe deg med å "mate" hånte avhengigheter inn i objektene dine i de aller fleste andre brukstilfeller.

Riktig bruk av DI tvinger deg til å "tenke i gjenstander." det holder kodeenhetene dine små, testbare og lett forståelige. Fordelene med dette kan ikke undervurderes. Når klassene tar ansvar, har de en tendens til å ha to ting:

  • Gjentakelse
  • Utviklingsvansker ved innføring av ny funksjonalitet

Etter hvert som greenfield-systemet ditt øker i dybden, kan du unngå at det også skaleres i kompleksitet ved å bruke avhengighetsinjeksjon for å holde koden gjenbruk høy.

Konklusjon

Få fart på testene og den totale leveringstiden ved å isolere kode på riktig måte – og avhengighetsinjeksjon er en velprøvd metode som kan hjelpe deg å gjøre det. Merk at Salesforce som plattform tilbyr mer avanserte verktøy, somStub API, som også kan hjelpe deg med å redusere testkompleksiteten. Stub API kan imidlertid bare erstatte metoder som er offentlig tilgjengelig på objektene dine, mens DI kan brukes til å erstatte kompleks funksjonalitet i de indre private metodene til klassene dine med hånet oppførsel.

Husk at når du jobber med eksisterende eller eldre kode, gjør "extract and refactor"-metoden som DI representerer deg i stand til å gjøre små, inkrementelle endringer som over tid kan fullstendig pusse opp eldre kode til noe som er bedre forstått, bedre testet og raskere !

Til slutt - avhengighetsinjeksjon er et emne med et stort overflateareal. Jeg har i denne artikkelen hovedsakelig fokusert på hvordan riktig bruk av DI kan hjelpe deg med å skrive godt testet og lett spottbar kode.Phillipe Özil har brukt mye tid i et tidligere innleggsnakker om DI fra en rekke andre vinkler, for eksempel hvordan inversjon av kontroll i kombinasjon med grensesnitt bidrar til å skrive løst koblet kode: perfekt for pakking og for å gi deklarative alternativer for å velge hvilken kode som skal kjøres.

Om forfatteren

James Simone er seniormedlem av teknisk stab hos Salesforce, og han har utviklet seg på Salesforce-plattformen siden 2015. Han har blogget siden slutten av 2019 om emnet Apex, Flow og mer iThe Joys Of Apex. Når han ikke skriver kode, liker han fjellklatring, baking av surdeigsbrød, løping med hunden sin og mer. Du kan følge med på livseventyrene hans klHun og Jim.

Effektiv avhengighetsinjeksjon i Apex (1)

References

Top Articles
Latest Posts
Article information

Author: Cheryll Lueilwitz

Last Updated: 10/21/2023

Views: 6130

Rating: 4.3 / 5 (74 voted)

Reviews: 89% of readers found this page helpful

Author information

Name: Cheryll Lueilwitz

Birthday: 1997-12-23

Address: 4653 O'Kon Hill, Lake Juanstad, AR 65469

Phone: +494124489301

Job: Marketing Representative

Hobby: Reading, Ice skating, Foraging, BASE jumping, Hiking, Skateboarding, Kayaking

Introduction: My name is Cheryll Lueilwitz, I am a sparkling, clean, super, lucky, joyous, outstanding, lucky person who loves writing and wants to share my knowledge and understanding with you.