kontakt@tujestbug.pl

Cześć! Zapewne nie raz zetknąłeś/zetknęłaś się w swojej pracy z narzędziem do ciągłej integracji takim jak np. popularny Jenkins. Stosujemy je w celu:

  • automatycznego przebudowania aplikacji i jej deployu na środowisko testowe
  • oraz np. w celach testowych do wykonania testów automatycznych (api/ui) już na testowanej wersji.

Nie tak dawno temu do grona tych narzędzi dołączył Github Actions. Posiada on istotne funkcje z perspektywy testera automatyzującego, o których chciałbym Ci nieco opowiedzieć w dzisiejszym artykule.

Spis treści

Co to jest Github Actions/Github Workflow?

Zacznijmy od rozłożenia tego hasła na jego składowe:

  • Git – jest to rozproszony system kontroli wersji, umożliwiającym równoległą pracę na tzw. ‘branchach’. Stanowi on różne wersje tej samej aplikacji. Jest to rozwiązanie open-source, darmowe, gdzie każdy może założyć sobie takie repozytorium kodu i trzymać tam swój projekt.
  • Github – jest to firma świadcząca usługę przechowywania repozytoriów w chmurze. Dzięki temu jako użytkownicy mamy do nich dostęp z każdego miejsca na świecie o dowolnym czasie.
  • Github Actions – jest to usługa firmy Github zapewniająca automatyzację kodu (czyli np. jego ciągłą integrację) oraz automatyzację zarządzania repozytorium znajdującym się na koncie użytkownika.

Co wyróżnia Github Actions na tle innych konkurencyjnych rozwiązań CI/CD?

Tak zwanym ‘game changerem’ w kontekście Github Actions jest wspomniana wcześniej automatyzacja zarządzania samym repozytorium. Czyli poza standardowymi mechanizmami CI/CD jak kompilacja kodu, uruchamianie testów czy deploye dochodzą tutaj funkcje związane bezpośrednio z kodem:

– Automatyczna recenzja kodu

– Zarządzanie pull requestami

– Automatyczne tworzenie pull requestów

Dzięki takiemu rozwiązaniu nie tylko możemy zautomatyzować proces budowy aplikacji już zaakceptowanej przez zespół projektowy, ale również wspomóc zespół w samym recenzowaniu i tworzeniu kodu.

Składowe Github workflow

Github Actions składają się z tzw. workflowów (stąd wymienna nazwa Github Workflow). W obrębie swojego darmowego konta możesz posiadać dowolną ilość repozytoriów (zarówno publicznych jak i prywatnych). Workflow jest to specjalnie zdefiniowany plik z rozszerzeniem .yml (yaml). Plik ten przechowuje szczegółową konfigurację zautomatyzowanych zadań na które składają się tzw. joby zawierające konkretne polecenia do wykonania w postaci stepów.

Dla każdego repozytorium możesz zdefiniować dowolną ilość workflowów, które będą składać się z dowolnej ilości tzw. ‘Jobów’ oraz ‘Stepów’. Najlepiej obrazuje to poniższy schemat:

Repozytorium Github

Workflow

Workflow używamy do automatyzacji zadań. Jest on przypisany do konkretnego repozytorium znajdującego się na GitHubie. Nie chcemy, aby te zadania cały czas się wykonywały, więc w ramach Workflow definiujemy tzw. ‘triggery’, inaczej wyzwalacze konkretnego workflow. Mogą to być następujące zdarzenia (‘eventy’):

– Utworzenie pull requestu,

– Commit lub push do repozytorium na wybrany branch,

– Harmonogram (czyli np. uruchamiaj się w każdy poniedziałek o godzinie 7:15),

– Manualne uruchomienie (czyli uruchomienie workflow na skutek manualnej akcji bezpośrednio na stronie GitHuba)

Job

Wraz w wybranym eventem zostanie uruchomiony job w ramach danego workflow. Zazwyczaj te joby uruchamiane są równolegle, ale można też skonfigurować proces, aby były wykonywane w drodze sekwencji. W ramach Joba będziemy definiować tzw. ‘Runner’, czyli środowisko uruchomieniowe dla naszego zadania. W ramach runnera definiujemy na jakiej maszynie chcemy uruchomić zadanie (np. github oferuje nam linuxy, macintoshe oraz windowsy) oraz z jakim obrazem dockerowym (prekonfigurowana maszyna wirtualna). Warto pamiętać, że mamy możliwość zdefiniowania kilku jobów w ramach danego workflow i niektóre z nich mogą się uruchamiać tylko gdy zajdą odpowiednie warunki.

Step

Najniższy poziom workflowu to step. To właśnie tutaj definiujemy precyzyjnie co konkretnie ma się zadziać w ramach automatyzacji. Do wyboru mamy wiersz konsoli poleceń (np. bash) oraz tak zwane akcje. Akcje (Action) to predefiniowane skrypty, które umożliwiają wykonanie określonych zadań jak np. pobranie repozytorium, wysłanie notyfikacji na slacka czy zarchiwizowanie plików (tzw. artefakty – np. wyniki testów). Akcje te dostępne są za darmo w marketplace Githubowym. Stepy uruchamiamy od pierwszego do ostatniego zdefiniowanego stepu w ramach danego joba. Część kroków podobnie jak w przypadku Joba może być uruchamiana warunkowo.

Zoptymalizuj Github Actions w swojej pracy

Automatyczna kompilacja kodu (przed mergem)

Często w naszej pracy musimy pracować na wielu wątkach. Z jednej strony możemy być bardzo zaangażowani w testowanie nowego wydania, weryfikować bugi z produkcji, a jeszcze gdzieś na boku kolanem dociskać nowe testy automatyczne. Może się przez to zdarzyć, że przeoczymy jakiś przecinek, średnik czy też nie dodamy potrzebnego pliku w utworzonym Pull Requeście. Wówczas grozi nam ryzyko, że te zmiany po prostu się nie skompilują. Jeśli taki merge przejdzie na początku dnia i szybko się zorientujemy, że coś nie gra to tak nic się nie stało. Gorzej się ma sprawa kiedy ten merge jest ostatnią akcją, którą zrobimy w pracy, a na noc zaplanowaliśmy wykonywanie ważnych testów automatycznych. W takiej sytuacji rano prawdopodobnie nie będziemy mieli ani jednego wyniku testu.

Co możemy zrobić? Z rozwiązaniem przychodzi nam Github Actions. Z jego pomocą możemy wymusić kompilację kodu przy utworzeniu i aktualizacji Pull Requestu. Dzięki temu nasz kod zostanie zweryfikowany zanim ktokolwiek kliknie przycisk 'Merge’.

Na początku workflowu definiujemy wydarzenia (eventy) jakie mają spowodować jego uruchomienie. W poniższym przykładzie chcemy uruchamiać workflow w momencie wypchnięcia (push) zmian do brancha main lub w przypadku utworzenia pull requestu właśnie do tego brancha.

				
					on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

				
			

W kolejnym kroku defniujemy job. Może to być np. ‘build’, który zostanie uruchomiony na najnowszej maszynie ubuntu

				
					jobs:
  build:
    runs-on: ubuntu-latest

				
			

Na sam koniec definiujemy stepy, czyli konkretne akcje, które mają zostać zrealizowane w ramach automatyzacji. W tej sytuacji będzie to pobranie wybranego wydania JDK i skompilowanie kodu za pomocą polecena mvn test z pominięciem testów. Te ostatnie nam nie będą w tym momencie potrzebne.

				
					steps:
  - uses: actions/checkout@v3
  - name: Set up JDK 17
    uses: actions/setup-java@v3
    with:
      java-version: '17'
      distribution: 'temurin'
      cache: maven
  - name: Build with Maven
    run: mvn test -DskipTests=true

				
			

Ostatecznie plik workflow będzie miał poniższą postać:

				
					name: Code compilation validation

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'temurin'
          cache: maven
      - name: Build with Maven
        run: mvn test -DskipTests=true

				
			

Kiedy już dodamy taki workflow, to w sytuacji gdy utworzymy nowy pull request do brancha ‘main’, Github automatycznie uruchomi zdefiniowany build i przekompiluje kod z repozytorium. Zielony przycisk ‘Merge’ zostanie ukryty na czas trwania zadania, jak pokazano niżej:

Gdy zadanie się nie powiedzie, pojawi się odpowiedni komunikat, a przycisk do zmergowania pozostanie wyszarzony. Dzięki takiemu rozwiązaniu znacząco zmniejszymy szanse na pojawienie się błędu kompilacji w głównym branchu!

Automatyczne sprawdzenie formatowania kodu

Czasami zdarza się, że każdy członek zespołu korzysta z innego narzędzia do pisania testów. Nie ma w tym nic złego, dopóki nie ingerują one w sposób formatowania samego kodu. Jeśli nawet uzgodniliśmy spójny sposób formatowania, ale niektórzy zapominają go przeformatować to wystarczy, że przed wystawieniem PRa sami przeformatujemy kod w rezultacie czego pojawi się wiele wcześniejszych zmian po poprzednich kontrybutorach. Takie dodatkowe zmiany będą utrudniać proces recenzji kodu i być może przyjdzie nam jeszcze poprawiać nie swoje zmiany.

Rozwiązanie? Wystarczy wymusić formatowanie kodu przez wszystkie osoby kontrybuujące do repozytorium. Najprościej będzie to osiągnąć weryfikacją, że kod zgłoszony do PR jest poprawnie sformatowany, dzięki czemu niesformatowany kod nigdy nie znajdzie się w repozytorium na głównym branchu.

Aby zweryfikować poprawność formatowania kodu posłużę się biblioteką Spotless (https://github.com/diffplug/spotless), która działa w dwóch trybach:

– apply (formatuje istniejący kod zgodnie z przyjętą konfiguracją),

– check (sprawdza czy istniejący kod jest sformatowany zgodnie z przyjętą konfiguracją).

Jak się domyślasz nas będzie interesować tryb ‘check’. Teraz pozostaje jedynie zmodyfikowanie poprzedniego workflow o uruchomienie komendy spotless zamiast przebudowy całego projektu.

Wynikowy workflow będzie wyglądał zatem tak:

				
					name: Code formatting verification

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'temurin'
          cache: maven
      - name: Verify code formatting
        run: mvn spotless:check

				
			

W tej sytuacji otrzymujemy 2 pliki workflow – jeden dla kompilacji, a drugi dla formatowania. Dzięki temu szybciej zdiagnozujemy co jest nie tak z PRem. Przykładowo w poniższej sytuacji mamy problem z formatowaniem, natomiast sam kod się kompiluje poprawnie:

formatowanie merge request

Oczywiście jest też możliwość ‘upchnięcia’ obu zadań w ramach 1 workflowu sprawdzającego PR pod kątem kompilacji oraz formatowania. Taki przykład możesz znaleźć u mnie na repozytorium: https://github.com/tnt-piotrd/tujestbug/blob/main/.github/workflows/maven.yml (szersze wyjaśnienie znajdziesz na filmie).

Uruchamianie testów z palca

Wyobraź sobie panel sterowania, z którego możesz uruchomić dowolną konfigurację swoich testów automatycznych. Tu i teraz, bez zbędnej zwłoki. Kilka kliknięć i masz przetestowaną całą grupę ficzerów na wybranym środowisku, przeglądarce, rozdzielczościach itd. Jakkolwiek fantazyjnie to brzmi, to mam dla Ciebie dobrą wiadomość – można to bardzo łatwo ogarnąć w Github Actions!

Kluczem do sukcesu jest tutaj zastosowanie specjalnego wyzwalacza dla workflowu jakim jest workflow_dispatch. Za jego pomocą możemy zdefiniować parametry uruchamiania dla naszych testów. Parametry mogą mieć flagę required (wymagany), być typu boolean (prawda/fałsz) czy też wartością z rozsuwanego menu (choice). 

W poniższym przykładzie definiuję przeglądarkę, rozdzielczość ekranu, środowisko testowe oraz zestaw testów do uruchomienia (testSuite):

				
					consoname: Tests on demand

on:
  workflow_dispatch:
    inputs:
      browser:
        description: 'Browser'
        required: true
        default: 'chrome'
        type: choice
        options:
          - chrome
          - firefox
          - edge
      resolution:
        description: 'Browser resolution'
        required: true
        default: "1920,1080"
        type: choice
        options:
          - "1920,1080"
          - "1280,720"
          - "1024,768"
      environment:
        description: 'Environment'
        required: true
        default: 'stage'
        type: choice
        options:
          - stage
          - uat
          - prod
      testSuite:
        description: 'Test Suite to execute'
        required: true
        default: 'RegressionTestSuite'
        type: choice
        options:
          - 'AdditionTestSuite'
          - 'MultiplicationTestSuite'
          - 'RegressionTestSuite'
          - 'SubtractionTestSuite'

				
			

Do uruchomienia testów na przeglądarce np. chrome konieczne jest posiadanie obrazu dockerowego z preinstalowanym Google Chromem. Ja skorzystam tu z poniższego obrazu:

				
					jobs:
  build:
    runs-on: ubuntu-latest
    container:
      image: markhobson/maven-chrome

				
			

Na koniec pozostaje już tylko wykonać checkout jak w porzednich krokach, następnie uruchomić testy z pobranymi parametrami i zarchiwizować rezultaty testów. Do tego ostatniego wykorzystam gotową akcje – actions/upload-artifact@v4

				
					steps:
  - uses: actions/checkout@v3
  - name: Set up JDK 17
    uses: actions/setup-java@v3
    with:
      java-version: '17'
      distribution: 'temurin'
      cache: maven
  - name: Run Test Suite
    run: mvn test -DtestSuite=${{inputs.testSuite}} -Dresolution=${{inputs.resolution}} -Dbrowser=${{inputs.browser}} -Denv=${{inputs.environment}}
  - name: Archive test results
    uses: actions/upload-artifact@v4
    with:
      name: test-report
      path: target/surefire-reports

				
			

Ostatecznie utworzony workflow będzie wyglądał następująco:

				
					name: Tests on demand

on:
  workflow_dispatch:
    inputs:
      browser:
        description: 'Browser'
        required: true
        default: 'chrome'
        type: choice
        options:
          - chrome
          - firefox
          - edge
      resolution:
        description: 'Browser resolution'
        required: true
        default: "1920,1080"
        type: choice
        options:
          - "1920,1080"
          - "1280,720"
          - "1024,768"
      environment:
        description: 'Environment'
        required: true
        default: 'stage'
        type: choice
        options:
          - stage
          - uat
          - prod
      testSuite:
        description: 'Test Suite to execute'
        required: true
        default: 'RegressionTestSuite'
        type: choice
        options:
          - 'AdditionTestSuite'
          - 'MultiplicationTestSuite'
          - 'RegressionTestSuite'
          - 'SubtractionTestSuite'

jobs:
  build:
    runs-on: ubuntu-latest
    container:
      image: markhobson/maven-chrome

    steps:
      - uses: actions/checkout@v3
      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'temurin'
          cache: maven
      - name: Run Test Suite
        run: mvn test -DtestSuite=${{inputs.testSuite}} -Dresolution=${{inputs.resolution}} -Dbrowser=${{inputs.browser}} -Denv=${{inputs.environment}}
      - name: Archive test results
        uses: actions/upload-artifact@v4
        with:
          name: test-report
          path: target/surefire-reports

				
			

Po umieszczeniu workflow w głównym branchu możemy się już cieszyć możliwością uruchomienia dowolnej konfiguracji testów z tzw. ‘palca’ bezpośrednio przez stronę GitHuba.

uruchomienie testów z palca na githubie

Jeżeli chciałbyś zobaczyć cały proces podpinania parametrów do testów automatycznych w Javie i Mavenie to zachęcam Cię do obejrzenia mojego wideo na YT

Optymalizacja optymalizacji

Jeśli zastosujesz któryś z wcześniej omówionych workflowów dla weryfikacji Pull Requestu, to z czasem możesz dojść do wniosku, że te weryfikacje (workflowy) wykonują się zbyt często. Czasami w repozytorium mamy więcej niż 1 projekt i nie zawsze zmiana będzie wymagać kompilacji i sprawdzenia formatowania. Może być przykładowo sytuacja kiedy tylko podmieniasz dane w pliku .csv albo dodajesz jakiś obrazek. W żaden sposób nie wpłynie to na proces kompilacji i formatowania, więc po co tracić czas i dodatkowo przepalać ograniczoną pulę minut CI/CD od Githuba?

Rozwiązanie jest bardzo trywialne, wystarczy ograniczyć zakres ścieżek – tzw. ‘paths’ dla których ma się wykonywać workflow.

				
					on:
  push:
    branches: [ "main" ]
    paths:
      - '**/src/**'
      - '**/pom.xml'
  pull_request:
    branches: [ "main" ]
    paths:
      - '**/src/**'
      - '**/pom.xml'

				
			

Kiedy utworzysz pull request, ale nie zmienisz ani głównego pliku pom.xml ani nie dokonasz żadnych zmian w katalogu źródłowym (src), to żaden z workflowów nie zostanie uruchomiony.

Tutaj możesz podejrzeć cały workflow na moim repozytorium: https://github.com/tnt-piotrd/tujestbug/blob/main/.github/workflows/maven.yml

Podsumowanie

W tym momencie wiesz jakich ja używam sposobów do optymalizacji swojej pracy z testami automatycznymi. Po przeczytaniu tego artykułu powinieneś wiedzieć:

  • Co to jest Github Actions
  • Co wyróżnia Github Actions na tle innych narzędzi CI/CD
  • Jakie są składowe Github actions (job, workflow, step)
  • Jak automatycznie kompilować kod na serwerach GitHuba
  • Jak można wymusić formatowanie kodu na zespole
  • Jak uruchamiać testy z palca bezpośrednio z strony GitHuba

Samego Github Actions można też oczywiście wykorzystać do uruchamiania testów zgodnie z narzuconym przez siebie harmonogramem (więcej o tym na filmie.)

A do czego Ty wykorzystujesz Github actions? Daj znać koniecznie w komentarzu!

Pozdrawiam!

Piotr


0 komentarzy

Dodaj komentarz

Avatar placeholder

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *