Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> [PHP] Testy jednostkowe w PHPUnit
eerie
post 12.03.2025, 17:57:09
Post #1





Grupa: Zarejestrowani
Postów: 109
Pomógł: 0
Dołączył: 3.08.2017

Ostrzeżenie: (0%)
-----


Na co powinienem zwrócić uwagę, pisząc testy w PHPUnit? Co powinienem testować, a czego nie?

Napisałem kiedyś taki test kontrolera w Symfony. Jak przetestować klikanie w linki bez Symfony?

Przeglądam pisanie testów w PHPUnit i nie widzę przykładów, jak to zrobić.

Podpowiedzcie mi, co powinienem przetestować w mojej aplikacji... smile.gif
Go to the top of the page
+Quote Post
nospor
post 13.03.2025, 10:06:46
Post #2





Grupa: Moderatorzy
Postów: 36 556
Pomógł: 6314
Dołączył: 27.12.2004




Cytat
Jak przetestować klikanie w linki bez Symfony?

Ja do tego uzywam Panther


--------------------

"Myśl, myśl, myśl..." - Kubuś Puchatek || "Manual, manual, manual..." - Kubuś Programista
"Szukaj, szukaj, szukaj..." - Kubuś Odkrywca || "Debuguj, debuguj, debuguj..." - Kubuś Developer

Go to the top of the page
+Quote Post
eerie
post 16.03.2025, 11:09:40
Post #3





Grupa: Zarejestrowani
Postów: 109
Pomógł: 0
Dołączył: 3.08.2017

Ostrzeżenie: (0%)
-----


Cytat
Ja do tego uzywam Panther

Zainstalowałem Panther Composer'em dla mojego projektu i mam problem. Gdy uruchamiam test w "Wierszu polecania"...

Kod
php vendor/bin/phpunit tests/

...wyświetla mi się "General error":

Cytat
'.' is not recognized as an internal or external command

Znalazłem na stackoverflow.com, aby użyć WSL2 "Windows Subsystem For Linux". Z poziomu okna MSYS2 uruchamiam test w PHPUnit...

Kod
/c/xampp/php/php ./vendor/bin/phpunit tests/

...i wywala mi ten sam błąd.

W czym tkwi problem? Bo użycie Bash'a w WSL nie pomaga i jakiś tam skrypt dalej wyrzuca mi ten błąd pod Windows... :| smile.gif

PS W pliku mojego testu MainPageControllerTest.php w linii 21 - wskazywanej jako problematyczna - jest kod:

Kod
$client->request('GET', '/');

...i to on przyczynia się do wyświetlenia błędu. Jak go zakomentuję/usunę, to błędu nie ma. Dziwne. smile.gif

PS2 Żeby było łatwiej, podaję wersje roboczą tej klasy:

Kod
<?php

declare(strict_types=1);

namespace App\Tests\Controller;

use Symfony\Component\Panther\PantherTestCase;

class MainPageControllerTest extends PantherTestCase
{
    public function testMainPageAndClickPolishLanguageLink(): void
    {
        // your app is automatically started using the built-in web server
        $client = static::createPantherClient();
        $client->request('GET', '/');
/*
        $client = self::createPantherClient([
            'hostname' => '127.0.0.9', // defaults to 127.0.0.1
            'port' => 8080, // defaults to 9080
        ]);
        $client->request('GET', '/');
*/
        // use any PHPUnit assertion, including the ones provided by Symfony...
        //$this->assertPageTitleContains('PHP Framework from EEQSOFT');

        [...]
    }
}


PS3 Wydaje mi się, że sprawę rozwiąże użycie Docker'a. Jutro sprawdzę. wink.gif

Ten post edytował eerie 16.03.2025, 11:09:59
Go to the top of the page
+Quote Post
nospor
post 17.03.2025, 12:43:50
Post #4





Grupa: Moderatorzy
Postów: 36 556
Pomógł: 6314
Dołączył: 27.12.2004




Cytat
ydaje mi się, że sprawę rozwiąże użycie Docker'a. Jutro sprawdzę.

No ja uzywam tego wlasnie na linux z dockerem i nie mialem takich problemow wiec trudno powiedziec co jest zle u ciebie. Daj znac jak ci poszlo z tym docker


--------------------

"Myśl, myśl, myśl..." - Kubuś Puchatek || "Manual, manual, manual..." - Kubuś Programista
"Szukaj, szukaj, szukaj..." - Kubuś Odkrywca || "Debuguj, debuguj, debuguj..." - Kubuś Developer

Go to the top of the page
+Quote Post
eerie
post 17.03.2025, 20:27:16
Post #5





Grupa: Zarejestrowani
Postów: 109
Pomógł: 0
Dołączył: 3.08.2017

Ostrzeżenie: (0%)
-----


Cytat
Daj znac jak ci poszlo z tym docker


Odpaliłem projekt na Docker'ze. Użyłem Composer'a, aby zainstalować biblioteki. I niby wszystko jest zainstalowane, włącznie z dbrekelmans/bdi, a przy uruchamianiu testu phpunit wyświetla mi się komunikat:

Cytat
Symfony\Component\Panther\Exception\RuntimeException: "geckodriver" binary not found. Install it using the package manager of your operating system or by running "composer require --dev dbrekelmans/dbi && vendor/bin/dbi detect drivers"


Jak wpisuję "composer require --dev dbrekelmans/db" czy "composer update dbrekelmans/db", to wyświetla się info, że jest zainstalowany. Natomiast polecenie "./vendor/bin/dbi detect drivers" nic nie zwraca. Pod Windows miałem jakiś komunikat, że jest ok. A tu nic... Coś jest nie tak, a ja jestem trochę "zielonkawy" z Linux'a. wink.gif
Go to the top of the page
+Quote Post
nospor
post 18.03.2025, 10:05:57
Post #6





Grupa: Moderatorzy
Postów: 36 556
Pomógł: 6314
Dołączył: 27.12.2004




Nie wiem, nigdy nie widzialem takich problemow. Podam ci co ja mam, moze ci to pomoze:

docker-compose.yml

version: '3'

services:
php8:
container_name: php8
build:
context: .
dockerfile: docker/images/php/Dockerfile
volumes:
- ./appcode:/var/www/html
- ./docker/images/php/conf/dev-php.ini:/usr/local/etc/php/conf.d/php-settings.ini

web:
container_name: web
ports:
- "8083:80"
build:
context: .
dockerfile: docker/images/nginx/Dockerfile
volumes:
- ./appcode:/var/www/html
- ./docker/images/nginx/conf:/tmp/nginx


PHP Dockerfile:
FROM php:8.4.1-fpm-alpine3.21

# Set the system locale and time
ENV TZ=Europe/London LANG=en_GB.UTF-8 LANGUAGE=en_GB.UTF-8 LC_ALL=en_GB.UTF-8 LC_CTYPE=en_GB.UTF-8
RUN apk update \
&& apk upgrade \
&& apk add --no-cache \
tzdata \
musl-locales \
musl-locales-lang \
&& ln -snf /usr/share/zoneinfo/$TZ /etc/localtime \
&& echo $TZ > /etc/timezone

# Install additional php extensions
ADD https://github.com/mlocati/docker-php-exten...-php-extensions /usr/local/bin/
RUN chmod +x /usr/local/bin/install-php-extensions \
&& sync \
&& install-php-extensions \
bcmath \
gd \
intl \
pcntl \
pdo_mysql \
xdebug \
zip

# Chromium and ChromeDriver
ENV PANTHER_NO_SANDBOX=1
ENV PANTHER_CHROME_ARGUMENTS='--disable-dev-shm-usage'
RUN apk add --no-cache chromium chromium-chromedriver

# Install Composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

# Install php-cs-fixer
RUN curl -L https://cs.symfony.com/download/php-cs-fixer-v3.phar -o php-cs-fixer \
&& chmod a+x php-cs-fixer \
&& mv php-cs-fixer /usr/local/bin/php-cs-fixer

# Clean up temporary files to keep image small
RUN rm -rf /var/cache/apk/* /var/tmp/* /tmp/*

# Configure the run.sh script to be executed
COPY docker/images/php/run.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/run.sh;

CMD ["/usr/local/bin/run.sh"]

composer.json
{
"type": "project",
"license": "proprietary",
"minimum-stability": "stable",
"prefer-stable": true,
"require": {
"php": ">=8.4",
"ext-ctype": "*",
"ext-iconv": "*",
"doctrine/annotations": "^1.0",
"doctrine/doctrine-bundle": "^2.10",
"doctrine/doctrine-migrations-bundle": "^3.2",
"doctrine/orm": "^2.12",
"guzzlehttp/guzzle": "^7.3",
"phpdocumentor/reflection-docblock": "^5.3",
"phpoffice/phpspreadsheet": "^1.29",
"phpstan/phpdoc-parser": "^1.6",
"symfony/asset": "7.2.*",
"symfony/console": "7.2.*",
"symfony/doctrine-messenger": "7.2.*",
"symfony/dotenv": "7.2.*",
"symfony/expression-language": "7.2.*",
"symfony/flex": "^2",
"symfony/form": "7.2.*",
"symfony/framework-bundle": "7.2.*",
"symfony/http-client": "7.2.*",
"symfony/intl": "7.2.*",
"symfony/mailer": "7.2.*",
"symfony/mime": "7.2.*",
"symfony/monolog-bundle": "^3.0",
"symfony/notifier": "7.2.*",
"symfony/process": "7.2.*",
"symfony/property-access": "7.2.*",
"symfony/property-info": "7.2.*",
"symfony/proxy-manager-bridge": "6.4.*",
"symfony/runtime": "7.2.*",
"symfony/security-bundle": "7.2.*",
"symfony/serializer": "7.2.*",
"symfony/string": "7.2.*",
"symfony/translation": "7.2.*",
"symfony/twig-bundle": "7.2.*",
"symfony/validator": "7.2.*",
"symfony/web-link": "7.2.*",
"symfony/webpack-encore-bundle": "^2.1",
"symfony/yaml": "7.2.*",
"twig/extra-bundle": "^2.12|^3.0",
"twig/twig": "^2.12|^3.0"
},
"config": {
"allow-plugins": {
"composer/package-versions-deprecated": true,
"symfony/flex": true,
"symfony/runtime": true
},
"optimize-autoloader": true,
"preferred-install": {
"*": "dist"
},
"sort-packages": true
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"App\\Tests\\": "tests/"
}
},
"replace": {
"symfony/polyfill-ctype": "*",
"symfony/polyfill-iconv": "*",
"symfony/polyfill-php72": "*",
"symfony/polyfill-php73": "*",
"symfony/polyfill-php74": "*",
"symfony/polyfill-php80": "*",
"symfony/polyfill-php81": "*"
},
"scripts": {
"auto-scripts": {
"cache:clear": "symfony-cmd",
"assets:install %PUBLIC_DIR%": "symfony-cmd"
},
"post-install-cmd": [
"@auto-scripts"
],
"post-update-cmd": [
"@auto-scripts"
]
},
"conflict": {
"symfony/symfony": "*"
},
"extra": {
"symfony": {
"allow-contrib": false,
"require": "7.2.*"
}
},
"require-dev": {
"dbrekelmans/bdi": "^1.0",
"phpunit/phpunit": "^9.5",
"symfony/browser-kit": "7.2.*",
"symfony/css-selector": "7.2.*",
"symfony/debug-bundle": "7.2.*",
"symfony/maker-bundle": "^1.0",
"symfony/panther": "^2.0",
"symfony/phpunit-bridge": "^7.2",
"symfony/stopwatch": "7.2.*",
"symfony/web-profiler-bundle": "7.2.*"
}
}

A tak odpalam testy z panther (e2e - tam trzymam testy dla panther)
docker exec -it --user=1000 php8 /bin/sh -c "php -d xdebug.mode=coverage bin/phpunit --testsuite e2e


--------------------

"Myśl, myśl, myśl..." - Kubuś Puchatek || "Manual, manual, manual..." - Kubuś Programista
"Szukaj, szukaj, szukaj..." - Kubuś Odkrywca || "Debuguj, debuguj, debuguj..." - Kubuś Developer

Go to the top of the page
+Quote Post
eerie
post 18.03.2025, 19:16:27
Post #7





Grupa: Zarejestrowani
Postów: 109
Pomógł: 0
Dołączył: 3.08.2017

Ostrzeżenie: (0%)
-----


Udało mi się rozwiązać ten problem. Wystarczyło dodać "geckodriver" [firefox'a] w Dockerfile:

Kod
[...]

RUN apt-get update                             \
&& apt-get install -y --no-install-recommends \
    ca-certificates curl firefox-esr           \
&& rm -fr /var/lib/apt/lists/*                \
&& curl -L https://github.com/mozilla/geckodriver/releases/download/v0.30.0/geckodriver-v0.30.0-linux64.tar.gz | tar xz -C /usr/local/bin \
&& apt-get purge -y ca-certificates curl

[...]


Niestety nie wiem, dlaczego test dla mojej klasy MainPageControllerTest pokazuje błąd, iż metoda assertPageTitleContains() jest niezdefiniowana.
Go to the top of the page
+Quote Post
nospor
post 19.03.2025, 08:53:59
Post #8





Grupa: Moderatorzy
Postów: 36 556
Pomógł: 6314
Dołączył: 27.12.2004




No bo nie ma tej metody?


--------------------

"Myśl, myśl, myśl..." - Kubuś Puchatek || "Manual, manual, manual..." - Kubuś Programista
"Szukaj, szukaj, szukaj..." - Kubuś Odkrywca || "Debuguj, debuguj, debuguj..." - Kubuś Developer

Go to the top of the page
+Quote Post
eerie
post 19.03.2025, 18:10:09
Post #9





Grupa: Zarejestrowani
Postów: 109
Pomógł: 0
Dołączył: 3.08.2017

Ostrzeżenie: (0%)
-----


Co mam zrobić, aby móc użyć tej metody? W dokumentacji Panther'a napisali, że można użyć dowolnej "PHPUnit Assertion", ale u mnie nie są dostępne... A klasa TestCase jest abstrakcyjna. Nie mogę utworzyć obiektu klasy TestCase, a przy tym - o ile dobrze pamiętam - PHP pozwala rozszerzać maksymalnie jedną klasę. Da się jakoś użyć "extends" dla klas "PantherTestCase" i "TestCase" jednocześnie? A może brakuje mi jakichś bibliotek i dlatego nie działa? Jestem w kłopocie... wink.gif smile.gif

Ten post edytował eerie 19.03.2025, 18:12:33
Go to the top of the page
+Quote Post
nospor
post 20.03.2025, 10:06:49
Post #10





Grupa: Moderatorzy
Postów: 36 556
Pomógł: 6314
Dołączył: 27.12.2004




Sam sobie komplikujesz rzeczy. Poprostu zobacz co ma Panther. Jesli chcesz sprawdzac tytul, to masz przeciez:

self::assertPageTitleSame


--------------------

"Myśl, myśl, myśl..." - Kubuś Puchatek || "Manual, manual, manual..." - Kubuś Programista
"Szukaj, szukaj, szukaj..." - Kubuś Odkrywca || "Debuguj, debuguj, debuguj..." - Kubuś Developer

Go to the top of the page
+Quote Post
eerie
post 20.03.2025, 19:33:13
Post #11





Grupa: Zarejestrowani
Postów: 109
Pomógł: 0
Dołączył: 3.08.2017

Ostrzeżenie: (0%)
-----


Udało się... Działa! Napisałem taki test. Ma to sens? smile.gif

Kod
<?php

declare(strict_types=1);

namespace App\Tests\Controller;

use Symfony\Component\BrowserKit\HttpBrowser;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\Panther\PantherTestCase;

class MainPageControllerTest extends PantherTestCase
{
    public function testMainPageTitleAndForm(): void
    {
        $browser = new HttpBrowser(HttpClient::create());

        $browser->request('GET', '/');
        $response = $browser->getResponse();

        preg_match(
            '/<title>(PHP Framework from EEQSOFT)<\/title>/',
            (string) $response,
            $matches
        );

        $this->assertEquals($matches[1], 'PHP Framework from EEQSOFT');

        $browser->clickLink('Polish');
        $response = $browser->getResponse();

        preg_match(
            '/<title>(PHP Framework od EEQSOFT)<\/title>/',
            (string) $response,
            $matches
        );

        $this->assertEquals($matches[1], 'PHP Framework od EEQSOFT');

        $browser->clickLink('Angielski');
        $browser->submitForm('Confirm', ['name' => 'Robert']);
        $response = $browser->getResponse();

        preg_match(
            '/(Welcome, Robert!)/',
            (string) $response,
            $matches
        );

        $this->assertEquals($matches[1], 'Welcome, Robert!');
    }
}
Go to the top of the page
+Quote Post
nospor
post 21.03.2025, 10:06:59
Post #12





Grupa: Moderatorzy
Postów: 36 556
Pomógł: 6314
Dołączył: 27.12.2004




Nie bardzo rozumiem po co uzywasz regexp i szukasz po calym response... Po to uzywasz Panther by sie nie bawic w takie rzeczy

Twoje Witaj Robert pewnie jest w jakim div o jakiejs klasie. Robisz wiec

$this->assertSelectorTextContains('#twojDIV', 'Witaj Robert');


--------------------

"Myśl, myśl, myśl..." - Kubuś Puchatek || "Manual, manual, manual..." - Kubuś Programista
"Szukaj, szukaj, szukaj..." - Kubuś Odkrywca || "Debuguj, debuguj, debuguj..." - Kubuś Developer

Go to the top of the page
+Quote Post
eerie
post 22.03.2025, 11:39:04
Post #13





Grupa: Zarejestrowani
Postów: 109
Pomógł: 0
Dołączył: 3.08.2017

Ostrzeżenie: (0%)
-----


Cytat
$this->assertSelectorTextContains('#twojDIV', 'Witaj Robert');

Metoda jest niezdefiniowana... Nie mogę jej użyć.

Kod
        preg_match(
            '/<p class="ok">\r\n    (Welcome, Robert!)<br>/',
            (string) $response,
            $matches
        );

        $this->assertEquals($matches[1], 'Welcome, Robert!');

Takie coś działa, ale nie wygląda to zbyt profesjonalnie. :]

PS Zdaje się, że w PHPUnit 9.6 część dawnych metod została usunięta, ale można użyć takie coś:

Kod
<?php

declare(strict_types=1);

namespace App\Tests\Controller;

use Symfony\Component\BrowserKit\HttpBrowser;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\Panther\PantherTestCase;

class MainPageControllerTest extends PantherTestCase
{
    public function testMainPageTitleAndForm(): void
    {
        $browser = new HttpBrowser(HttpClient::create());

        $browser->request('GET', '/');
        $response = $browser->getResponse();

        $this->assertStringContainsString(
            '<title>PHP Framework from EEQSOFT</title>',
            (string) $response
        );

        $browser->clickLink('Polish');
        $response = $browser->getResponse();

        $this->assertStringContainsString(
            '<title>PHP Framework od EEQSOFT</title>',
            (string) $response
        );

        $browser->clickLink('Angielski');
        $browser->submitForm('Confirm', ['name' => 'Robert']);
        $response = $browser->getResponse();

        $this->assertStringContainsString(
            'Welcome, Robert!',
            (string) $response
        );
    }
}


Ten post edytował eerie 22.03.2025, 11:53:01
Go to the top of the page
+Quote Post
nospor
post 24.03.2025, 10:03:04
Post #14





Grupa: Moderatorzy
Postów: 36 556
Pomógł: 6314
Dołączył: 27.12.2004




Cytat
Metoda jest niezdefiniowana... Nie mogę jej użyć.

To uzyj innej, podobnej, moze zmienili nazwe troche czy cos takiego


--------------------

"Myśl, myśl, myśl..." - Kubuś Puchatek || "Manual, manual, manual..." - Kubuś Programista
"Szukaj, szukaj, szukaj..." - Kubuś Odkrywca || "Debuguj, debuguj, debuguj..." - Kubuś Developer

Go to the top of the page
+Quote Post
eerie
post 24.03.2025, 11:43:05
Post #15





Grupa: Zarejestrowani
Postów: 109
Pomógł: 0
Dołączył: 3.08.2017

Ostrzeżenie: (0%)
-----


Cytat(nospor @ 24.03.2025, 10:03:04 ) *
To uzyj innej, podobnej, moze zmienili nazwe troche czy cos takiego

Przeszukałem wszystkie assertions w dokumentacji i nic podobnego nie znalazłem. wink.gif smile.gif

PS Wygląda mi, jakby pousuwali sporo metod i ostro to uprościli.
Go to the top of the page
+Quote Post
nospor
post 24.03.2025, 11:50:19
Post #16





Grupa: Moderatorzy
Postów: 36 556
Pomógł: 6314
Dołączył: 27.12.2004




Ale czemu ty tego szukasz w phpunit?

To jest w Panther a nie w phpunit
https://github.com/symfony/panther/blob/mai...rtionsTrait.php


--------------------

"Myśl, myśl, myśl..." - Kubuś Puchatek || "Manual, manual, manual..." - Kubuś Programista
"Szukaj, szukaj, szukaj..." - Kubuś Odkrywca || "Debuguj, debuguj, debuguj..." - Kubuś Developer

Go to the top of the page
+Quote Post
eerie
post 24.03.2025, 14:21:18
Post #17





Grupa: Zarejestrowani
Postów: 109
Pomógł: 0
Dołączył: 3.08.2017

Ostrzeżenie: (0%)
-----


Przeanalizowałem klasę PantherTestCase dla v2.0.0 mojej instalacji Panther'a. Bez instalacji FrameworkBundle Symfony klasa WebTestCase jest niedostępna i klasa abstrakcyjna PantherTestCase rozszerza TestCase PHPUnit oraz używa PantherTestCaseTrait, a nie WebTestAssertionsTrait. Przez to metody takie jak assertSelectorTextContains() są niedostępne. Nie wiem, czy powiniennem zainstalować specjalnie FrameworkBundle... I czy to będzie działać? Prościej będzie użyć metody assertStringContainsString() z PHPUnit, jak ja to zrobiłem w moim teście MainPageControllerTest.

Metoda assertSelectorTextContains() i tak używa assertStringContainsString():

Kod
    public static function assertSelectorTextContains(string $selector, string $text, string $message = ''): void
    {
        self::assertStringContainsString($text, self::getText($selector), $message);
    }

Dlatego można spokojnie użyć tej metody... Tak myślę...

Kod
        $this->assertStringContainsString(
            '<title>PHP Framework from EEQSOFT</title>',
            (string) $response
        );
Go to the top of the page
+Quote Post
nospor
post 24.03.2025, 14:32:22
Post #18





Grupa: Moderatorzy
Postów: 36 556
Pomógł: 6314
Dołączył: 27.12.2004




Aha. To by wyjasnialo pare rzeczy biggrin.gif


--------------------

"Myśl, myśl, myśl..." - Kubuś Puchatek || "Manual, manual, manual..." - Kubuś Programista
"Szukaj, szukaj, szukaj..." - Kubuś Odkrywca || "Debuguj, debuguj, debuguj..." - Kubuś Developer

Go to the top of the page
+Quote Post
eerie
post 14.04.2025, 11:23:15
Post #19





Grupa: Zarejestrowani
Postów: 109
Pomógł: 0
Dołączył: 3.08.2017

Ostrzeżenie: (0%)
-----


Napisałem kilka testów dla klas source'a. Nie da się zbytnio przetestować Controller'ów i Service'ów, przynajmniej ja nie wiem jak, więc użyłem HttpBrowser. Przetestowałem też Repository, ale moje testy ingerują w dane bazy danych. Nie wiem, czy tak można? Przetestowałem też Validator'a z użyciem Mock'a dla klasy CsrfToken, która zapisuje token w sesji... Czy macie uwagi? Popełniłem rażące błędy, które wypada poprawić?

PS Wypadałoby chyba przetestować jeszcze Core'a? Czyli rdzenne klasy mojego framework'a... smile.gif
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
1 Użytkowników czyta ten temat (1 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Wersja Lo-Fi Aktualny czas: 29.04.2025 - 07:37