doudeng5764 2015-11-21 16:33
浏览 132
已采纳

使用PHPUnit和Selenium设置测试

Can you help me with setting up my test enviroment please. I'm running on Ubuntu, have selenium web server installed (and running), and trought PHPUnit I'm executing my tests. Most propably I've got stucked on some small error, but I have no I idea how to fix it now.

my code is simple

class WebTest extends PHPUnit_Extensions_Selenium2TestCase 
{
protected function setUp()

{
    $this->setBrowser('firefox');
    $this->setBrowserUrl('http://www.google.com/');
}

public function testTitle()
{
    $this->url('http://www.google.com/');
    $this->assertEquals('google', $this->title());
}

but getting this error

PHP Fatal error: Class 'PHPUnit_Extensions_Selenium2TestCase' not found in /home/jozef/vendor/phpunit/phpunit-selenium/WebTest.php on line 4

Selenium I have installed

Can you please help me move on ? Thank you :)

  • 写回答

2条回答 默认 最新

  • duanpi7107 2016-06-17 21:49
    关注

    Here is instruction how to run Selenium IDE test recorded on Firefox on phpUnit, MacOS, laravel 5.2, firefox. I also show how to set up screenshot here (i also set up Laravel to heve access to DB to clean it up after test ends):

    In your test-s directory, create selenium directory. And create files: SeleniumClearTestCase.php

    class SeleniumClearTestCase extends MigrationToSelenium2 // Poniewaz testy seleniumIDE są zapisane w starym formacie (selenium 1) to urzywamy tej przejsciowki.
    {
        protected $baseUrl = 'http://yourservice.dev';
    
        protected function setUp()
        {
            $screenshots_dir = __DIR__.'/screenshots';
            if (! file_exists($screenshots_dir)) {
                mkdir($screenshots_dir, 0777, true);
            }
            $this->listener = new PHPUnit_Extensions_Selenium2TestCase_ScreenshotListener($screenshots_dir);
    
            $this->setBrowser('firefox');
            $this->setBrowserUrl($this->baseUrl);
            $this->createApplication(); // bootstrap laravel app
        }
    
        public function onNotSuccessfulTest($e)
        {
            $this->listener->addError($this, $e, null);
            parent::onNotSuccessfulTest($e);
        }
    
        /**
         * Wykonaj screenshot w danym mommencie.
         * @return
         */
        public function screenshot()
        {
            $this->listener->addError($this, new Exception, null); // ta funkcja troche myli nazwą, ale wykona ona tylko screenshota nic ponadto
        }
    
        /**
         * Creates the application.
         *
         * @return \Illuminate\Foundation\Application
         */
        public function createApplication()
        {
            $app = require __DIR__.'/../../bootstrap/app.php';
    
            $app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
    
            return $app;
        }
    }
    

    Next file: MigrationToSelenium2.php (from github but I add some moficiations):

    <?php
    /*
     * Copyright 2013 Roman Nix
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     * http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    /**
     * Implements adapter for migration from PHPUnit_Extensions_SeleniumTestCase
     * to PHPUnit_Extensions_Selenium2TestCase.
     *
     * If user's TestCase class is implemented with old format (with commands
     * like open, type, waitForPageToLoad), it should extend MigrationToSelenium2
     * for Selenium 2 WebDriver support.
     */
    abstract class MigrationToSelenium2 extends LaravelTestCase // MY modification - extends diffrent class. If you don't want use laravel, extends this class by PHPUnit_Extensions_Selenium2TestCase
    {
        public function open($url)
        {
            $this->url($url);
        }
    
        public function type($selector, $value)
        {
            $input = $this->byQuery($selector);
            $input->value($value);
        }
    
        protected function byQuery($selector)
        {
            if (preg_match('/^\/\/(.+)/', $selector)) {
                /* "//a[contains(@href, '?logout')]" */
                return $this->byXPath($selector);
            } elseif (preg_match('/^([a-z]+)=(.+)/', $selector, $match)) {
                /* "id=login_name" */
                switch ($match[1]) {
                    case 'id':
                        return $this->byId($match[2]);
                        break;
                    case 'name':
                        return $this->byName($match[2]);
                        break;
                    case 'link':
                        return $this->byPartialLinkText($match[2]);
                        break;
                    case 'xpath':
                        return $this->byXPath($match[2]);
                        break;
                    case 'css':
                        $cssSelector = str_replace('..', '.', $match[2]);
    
                        return $this->byCssSelector($cssSelector);
                        break;
    
                }
            }
            throw new Exception("Unknown selector '$selector'");
        }
    
        protected function waitForPageToLoad($timeout)
        {
            $this->timeouts()->implicitWait((int) $timeout); // MY modification - cast to 'int'
        }
    
        public function click($selector)
        {
            $input = $this->byQuery($selector);
            $input->click();
        }
    
        public function select($selectSelector, $optionSelector)
        {
            $selectElement = parent::select($this->byQuery($selectSelector));
            if (preg_match('/label=(.+)/', $optionSelector, $match)) {
                $selectElement->selectOptionByLabel($match[1]);
            } elseif (preg_match('/value=(.+)/', $optionSelector, $match)) {
                $selectElement->selectOptionByValue($match[1]);
            } else {
                throw new Exception("Unknown option selector '$optionSelector'");
            }
        }
    
        public function isTextPresent($text)
        {
            if (strpos($this->byCssSelector('body')->text(), $text) !== false) {
                return true;
            } else {
                return false;
            }
        }
    
        public function isElementPresent($selector)
        {
            $element = $this->byQuery($selector);
            if ($element->name()) {
                return true;
            } else {
                return false;
            }
        }
    
        public function getText($selector)
        {
            $element = $this->byQuery($selector);
    
            return $element->text();
        }
    
        /** MY MODIFICATION (support for getEval)
         * Funkcja wykonuje kod js i jest uzywana w testach selenium IDE np. w funkcji 'storeEval'.
         * @param  string $javascriptCode Kod w JS np. "storedVars['registerurl'].match(/[^\\/]+$/)"
         * @param  [type] $args           tablica asocjacyjna klucz wartość z wartościami
         *                                jakie mają się znaleźć w zmiennej storedVars. np.
         *                                $args=['registerurl'=>'http://example.com']
         * @return string or array        jeżeli rezultat JS to string/liczba to zwraca je jak są
         *                                              jeżeli rezultat JS to tablica, to zwraca tablicę.
         */
        public function getEval($javascriptCode, $args)
        {
            $sv = 'storedVars=[]; ';
            foreach ($args as $key => $val) {
                $sv = $sv."storedVars['".$key."']='".$val."'; ";
            }
    
            $result = $this->execute(['script' => $sv.' return '.$javascriptCode, 'args' => []]);
    
            return $result;
        }
    }
    

    Next file: LaravelTestCase.php this is exact copy of Illuminate\Foundation\Testing\TestCase but it not extends PHPUnit_Framework_TestCase, but PHPUnit_Extensions_Selenium2TestCase class.

    Last file: in test directory create file testrunner (which is bash script):

    seleniumIsRun=`ps | grep -w selenium.jar | grep -v grep | wc -l`
    if (( $seleniumIsRun == 0 )); then    # run selenium server if it is not run already
        java -jar ./tests/selenium/selenium.jar &
        sleep 5s
    fi
    rm -r ./tests/selenium/screenshots
    php artisan db:seed    # reset DB using laravel (my laravel seeders clean db at the begining)
    vendor/bin/phpunit  # run php unit (in laravel it is in this direcotry)
    

    Next step, download newest "Selenium Standalone Server" from http://www.seleniumhq.org/download/ and change it's name and copy it to tests/selenium/selenium.jar.

    Next step, if you don't have java command in console install newerst JDK from http://www.oracle.com/technetwork/java/javase/downloads/index.html

    LARAVEL

    In composer.json update sections (add: phpunit/phpunit-selenium (github) and our new classes)

    "require-dev": {
        "fzaninotto/faker": "~1.4",
        "mockery/mockery": "0.9.*",
        "phpunit/phpunit": "~4.0",
        "symfony/css-selector": "2.8.*|3.0.*",
        "symfony/dom-crawler": "2.8.*|3.0.*",
        "phpunit/phpunit-selenium": "> 1.2"
    },
    
    "autoload-dev": {
        "classmap": [
            "tests/selenium/SeleniumClearTestCase.php",
            "tests/selenium/MigrationToSelenium2.php",
            "tests/selenium/LaravelTestCase.php",
            "tests/TestCase.php"
        ]
    },
    

    Then run

    composer update
    

    and

    composer dump-autoload
    

    Ok now we have all files to setUp selenium and phpunit. So let's make some tests using plugin Selenium IDE in firefox, we also need to install 'Selenium IDE: PHP Formatters' plugin for save test as phpunit. When we record test, and we check that it works and we save it as phpunit (and we can also save test in native html selenium format .se - to have 'source' of our php test, and be able in future to run it in selenium IDE by hand in futre...) - then we copy it to folder test/selenium/tests. And then we change test by remove setUp part, and also change extension class to SeleniumClearTestCase. For instance we can create test like this:

    <?php
    
    class LoginTest extends SeleniumClearTestCase
    {
        public function testAdminLogin()
        {
            self::adminLogin($this);
        }
    
        public function testLogout()
        {
            self::adminLogin($this);
    
            //START POINT: User zalogowany
            self::logout($this);
        }
    
        public static function adminLogin($t)
        {
            self::login($t, 'jan.kowalski@gmail.com', 'secret_password');
            $t->assertEquals('Jan Kowalski', $t->getText('css=span.hidden-xs'));
        }
    
        // @source LoginTest.se
        public static function login($t, $login, $pass)
        {
            $t->open('/');
            $t->click("xpath=(//a[contains(text(),'Panel')])[2]");
            $t->waitForPageToLoad('30000');
            $t->type('name=email', $login);
            $t->type('name=password', $pass);
            $t->click("//button[@type='submit']");
            $t->waitForPageToLoad('30000');        
        }
    
        // @source LogoutTest.se
        public static function logout($t)
        {
            $t->click('css=span.hidden-xs');
            $t->click('link=Wyloguj');
            $t->waitForPageToLoad('30000');
            $t->assertEquals('PANEL', $t->getText("xpath=(//a[contains(text(),'Panel')])[2]"));
        }
    }
    

    As you can see I put parts that can be reusable to separate STATIC functions. And below more Complicated test which use that static functions (which also clen up DB):

    <?php
    use App\Models\Device;
    use App\Models\User;
    
    class DeviceRegistrationTest extends SeleniumClearTestCase
    {
        public function testDeviceRegistration()
        {
          $email = 'paris@gmail.com';
          self::registerDeviceAndClient($this,'Paris','Hilton',$email,'verydifficultpassword');
          self::cleanRegisterDeviceAndClient($email);
        }
    
        // ------- STATIC elements
    
        public static function registerDeviceAndClient($t,$firstname, $lastname, $email, $pass) {
          LoginTest::adminLogin($t);
    
          // START POINT: User zalogowany jako ADMIN
          $t->click('link=Urządzenia');
          $t->click('link=Rejestracja');
          $t->waitForPageToLoad('30000');
          $registerurl = $t->getText('css=h4');
          $token = $t->getEval("storedVars['registerurl'].match(/[^\\/]+$/)", compact('registerurl'))[0];
          $t->screenshot();
                             // LOG OUT ADMIn
          LoginTest::logout($t);
                            // Otwórz link do urzadzenia
          $t->open($registerurl);
          $t->click('link=Rejestracja');
          $t->waitForPageToLoad('30000');
          $t->type('name=email', $email);
          $t->screenshot(); // take some photo =)
          $t->click('css=button.button-control');
          $t->waitForPageToLoad('30000');
          // Symuluj klikniecie w link aktywacyjny z emaila
          $t->open('http://yourdomain.dev/rejestracja/'.$token);
          $t->type('name=firstname', $firstname);
          $t->type('name=lastname', $lastname);
          $t->type('name=password', $pass);
          $t->type('name=password_confirmation', $pass);
          $t->screenshot(); // powinno byc widac formularz rejestracyjny nowego klienta
          $t->click("//button[@type='submit']");
          $t->waitForPageToLoad('30000');
          // Asercje
          $t->assertEquals($firstname.' '.$lastname, $t->getText('css=span.hidden-xs'));
        }
    
        public static function cleanRegisterDeviceAndClient($email) {
          $user = User::where('email','=',$email)->first();
          $device = Device::where('client_user_id','=',$user->id);
          $device->forceDelete();
          $user->forceDelete();
        }
    }
    

    And You run test by

    ./testrunner

    enjoy :)

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 如何让企业微信机器人实现消息汇总整合
  • ¥50 关于#ui#的问题:做yolov8的ui界面出现的问题
  • ¥15 如何用Python爬取各高校教师公开的教育和工作经历
  • ¥15 TLE9879QXA40 电机驱动
  • ¥20 对于工程问题的非线性数学模型进行线性化
  • ¥15 Mirare PLUS 进行密钥认证?(详解)
  • ¥15 物体双站RCS和其组成阵列后的双站RCS关系验证
  • ¥20 想用ollama做一个自己的AI数据库
  • ¥15 关于qualoth编辑及缝合服装领子的问题解决方案探寻
  • ¥15 请问怎么才能复现这样的图呀