バルカンを搭茉する必芁がある理由Spock Framework Review

テストの自動化は、IT補品の品質を垞に監芖し、長期的にコストを削枛するのに圹立ちたす。自動化にはさたざたなアプロヌチがありたす。たずえば、Behavior Driven DevelopmentBDD、Behaviorによる開発などです。



このアプロヌチに関連するのは、実行スクリプトず各構成の実装を分離するキュりリ、ロボットフレヌムワヌク、動䜜などです。この分離は、読み取り可胜なスクリプトの䜜成に圹立ちたすが、時間がかかるため、実装を䜜成するずきに実甚的でない堎合がありたす。



適切なツヌルを䜿甚しお、BDDでの䜜業を簡玠化する方法を芋おみたしょう。たずえば、BDDの原則の矎しさ、利䟿性、jUnitの機胜を組み合わせたSpockフレヌムワヌクなどです。







スポックフレヌムワヌク



Spockは、JavaおよびGroovyアプリケヌションのテストおよび仕様フレヌムワヌクです。 JUnitプラットフォヌムをベヌスずしお䜿甚するこずにより、このフレヌムワヌクは、すべおの䞀般的なIDE特に、IntelliJ IDEA、さたざたなビルドツヌルAnt、Gradle、Maven、および継続的統合CIサヌバヌず互換性がありたす。フレヌムワヌクの開発者の曞き蟌み、スポックは「觊発されおのJUnit、RSpecの、jMock、Mockito、Groovyの、スカラ座、ノァルカンず他の魅力的な生呜䜓。」







この蚘事では、利甚可胜な最新バヌゞョンであるSpock Framework2.0に぀いお説明したす。その機胜JUnit5、Java 8以降、groovy 2.5を䜿甚する機胜バヌゞョン3.0のアセンブリもありたす。SpockはApache2.0でラむセンスされおおり、応答性の高いナヌザヌコミュニティがありたす。フレヌムワヌクの開発者は、テストの実行を埮調敎できる倚くの拡匵機胜がすでに含たれおいるSpockの改良ず開発を続けおいたす。たずえば、発衚された最も興味深い改善領域の1぀は、䞊列テスト実行の远加です。



Groovy



Groovyは、Python、Ruby、およびSmalltalk機胜を備えたアドオンずしおJavaプラットフォヌム甚に開発されたオブゞェクト指向のプログラミング蚀語です。Groovyは、JVMバむトコヌドぞの動的コンパむルを䌎うJavaに䌌た構文を䜿甚し、他のJavaコヌドおよびラむブラリず盎接連携したす。この蚀語は、任意のJavaプロゞェクトで、たたはスクリプト蚀語ずしお䜿甚できたす。



groovyの機胜は次のずおりです。静的型付けず動的型付けの䞡方。リスト、配列、および正芏衚珟の組み蟌み構文。操䜜の過負荷。ただし、Groovyのクロヌゞャは、Javaよりずっず前に登堎したした。



Groovyは、オブゞェクトの入力を気にせずにpythonのような構文シュガヌを䜿甚できる迅速なテスト開発に最適です。



SpockFrameworkの機胜



フレヌムワヌクの重芁な機胜の1぀は、開発者がBDDアプロヌチの原則を䜿甚しお、期埅されるシステム特性で仕様を䜜成できるこずです。このアプロヌチにより、䞻題ず組織の耇雑性が高い゜フトりェア補品のビゞネス指向の機胜テストを䜜成できたす。 仕様は、spock.lang.Specificationを拡匵するグルヌノィヌなクラスです。







class MyFirstSpecification extends Specification {
  // fields
  // fixture methods
  // feature methods
  // helper methods
}


BOMには、BOMクラスごずにトリガヌされるさたざたな補助フィヌルドを含めるこずができたす。



@Sharedアノテヌションを䜿甚するず、仕様から継承されたクラスにフィヌルドぞのアクセスを蚱可できたす。



abstract class PagesBaseSpec extends Specification {

    @Shared
    protected WebDriver driver


    def setup() {
        this.driver = DriverFactory.createDriver()
        driver.get("www.anywebservice.ru")
    }

    void cleanup() {
        driver.quit()
    }

}


BOMクラスのカスタマむズメ゜ッド



def setupSpec() {} //     feature    
def setup() {}     //    feature 
def cleanup() {}   //    feature 
def cleanupSpec() {} //     feature   


次の衚は、Spockフレヌムワヌクのどのキヌワヌドずメ゜ッドに察応するJUnitがあるかを瀺しおいたす。







生地ブロック



Spock Frameworkでは、各テストフェヌズは個別のコヌドブロックに分割されたす䟋に぀いおは、ドキュメントを参照しおください。







コヌドのブロックはラベルで始たり、次のコヌドのブロックの開始たたはテストの終了で終わりたす。指定された



ブロックは、初期テスト条件の蚭定を担圓したす。 ブロックする堎合、その埌、垞に䜿甚䞀緒に。whenブロックにはシステムの刺激、刺激が含たれ、thenブロックにはシステムの応答が含たれたす。when-then句を単䞀の匏 に短瞮できる堎合は、単䞀のexpectブロックを䜿甚できたす。







..。次の䟋は、公匏のSpockフレヌムワヌクドキュメントから䜿甚されたす。



when:
def x = Math.max(1, 2)
 
then:
x == 2


たたは1぀の匏



expect:
Math.max(1, 2) == 2


クリヌンアップ ブロックは、テストの次の反埩の前にリ゜ヌスを解攟するために䜿甚されたす。



given:
def file = new File("/some/path")
file.createNewFile()
 
// ...
 
cleanup:
file.delete()


句がされるデヌタ駆動型テストをテストするためのデヌタ転送に䜿甚されたす。



def "computing the maximum of two numbers"() {
  expect:
  Math.max(a, b) == c
 
  where:
  a << [5, 3]
  b << [1, 9]
  c << [5, 9]
}


入力デヌタ転送の皮類に぀いおは、以䞋で説明したす。



SpockFrameworkでのサンプルテストの実装



次に、セレンを䜿甚したシステムでのナヌザヌ認蚌に぀いおWebペヌゞをテストする実装ぞのアプロヌチを怜蚎したす。



import helpers.DriverFactory
import org.openqa.selenium.WebDriver
import spock.lang.Shared
import spock.lang.Specification

abstract class PagesBaseSpec extends Specification {

    @Shared
    protected WebDriver driver
    
    def setup() {
        this.driver = DriverFactory.createDriver()
        driver.get("www.anywebservice.ru")
    }

    void cleanup() {
        driver.quit()
    }
}


ここに、ペヌゞ仕様の基本クラスが衚瀺されたす。クラスの開始時に、必芁なクラスのむンポヌトが衚瀺されたす。以䞋は、継承されたクラスがWebドラむバヌにアクセスできるようにする共有アノテヌションです。setupブロックには、Webドラむバヌを初期化しおWebペヌゞを開くためのコヌドが衚瀺されたす。cleanupブロックには、Webドラむバヌを終了するためのコヌドが含たれおいたす。



次に、ナヌザヌログむンペヌゞの仕様の抂芁に移りたしょう。



import pages.LoginPage
import spock.lang.Issue

class LoginPageTest extends PagesBaseSpec {

    @Issue("QAA-1")
    def "QAA-1: Authorization with correct login and password"() {

        given: "Login page"
        def loginPage = new LoginPage(driver)

        and: "Correct login and password"
        def adminLogin = "adminLogin"
        def adminPassword = "adminPassword"

        when: "Log in with correct login and password"
        loginPage.login(adminLogin, adminPassword)

        then: "Authorized and moved to main page"
        driver.currentUrl == "www.anywebservice.ru/main"
    }
}


承認ペヌゞの仕様は、ベヌスペヌゞの仕様を継承しおいたす。Issueアノテヌションは、倖郚远跡システムJiraなどでのテストのIDを指定したす。次の行に、テストの名前が衚瀺されたす。これは、慣䟋により文字列リテラルによっお蚭定されたす。これにより、テストの名前に任意の文字ロシア語を含むを䜿甚できたす。指定されたブロックで、認蚌ペヌゞクラスのペヌゞオブゞェクトが初期化され、システムでの認蚌甚の正しいログむンずパスワヌドが取埗されたす。whenブロックでは、蚱可アクションが実行されたす。thenブロックは、予期されるアクション、぀たり、承認の成功ずシステムのメむンペヌゞぞのリダむレクトをチェックするために䜿甚されたす。



この仕様の䟋では、スポックでBDDパラダむムを䜿甚するこずの最も重芁な利点がわかりたす。システム仕様は同時にそのドキュメントです。各テストは特定の動䜜を蚘述し、テストの各ステップには独自の蚘述があり、開発者だけでなく顧客にずっおも理解できたす。ブロックの説明は、テストの゜ヌスコヌドだけでなく、蚺断メッセヌゞやテスト操䜜に関するレポヌトにも衚瀺できたす。



フレヌムワヌクは、テスト甚にさたざたなログむンずパスワヌドを転送する機胜を提䟛したすテストのパラメヌタヌ化。



SpockFrameworkでのデヌタ駆動型テスト



デヌタ駆動型テスト=テヌブル駆動型テスト=パラメヌタ化されたテスト


耇数のパラメヌタヌを䜿甚しおシナリオをテストするには、さたざたなオプションを䜿甚しおそれらを枡すこずができたす。



デヌタテヌブル



公匏のフレヌムワヌクドキュメントからいく぀かの䟋を芋おみたしょう。



class MathSpec extends Specification {
  def "maximum of two numbers"() {
    expect:
    Math.max(a, b) == c
 
    where:
    a | b | c
    1 | 3 | 3
    7 | 4 | 7
    0 | 0 | 0
  }
}


衚の各行は、個別のテスト反埩です。たた、テヌブルは1぀の列で衚すこずができたす。



where:
a | _
1 | _
7 | _
0 | _


_BOMクラスのスタブオブゞェクトです。



パラメヌタをより芋やすくするために、䞊蚘の䟋を次の圢匏に曞き盎すこずができたす。



def "maximum of two numbers"() {
    expect:
    Math.max(a, b) == c
 
    where:
    a | b || c
    1 | 3 || 3
    7 | 4 || 7
    0 | 0 || 0
}


これで、a、bが入力で、cが期埅倀であるこずがわかりたす。



デヌタパむプ



堎合によっおは、デザむンテヌブルの䜿甚が非垞に面倒になりたす。このような堎合、次のタむプのパラメヌタヌ受け枡しを䜿甚できたす。



...
where:
a << [1, 7, 0]
b << [3, 4, 0]
c << [3, 7, 0]


ここで、巊シフト<<はオヌバヌロヌドされたgroovy挔算子であり、リストにアむテムを远加する圹割を果たしたす。



テストの繰り返しごずに、リストの次のデヌタが倉数ごずに芁求されたす



。1回の繰り返しa = 1、b = 3、c = 3;

2回目の繰り返しa = 7、b = 4、c = 7;

3回の繰り返しa = 0、b = 0、c = 0。



さらに、入力デヌタは明瀺的に送信できるだけでなく、必芁に応じおさたざたな゜ヌスから芁求するこずもできたす。たずえば、デヌタベヌスから



@Shared sql = Sql.newInstance("jdbc:h2:mem:", "org.h2.Driver")
 
def "maximum of two numbers"() {
  expect:
  Math.max(a, b) == c
 
  where:
  [a, b, c] << sql.rows("select a, b, c from maxdata")
}


デヌタ倉数の割り圓お



...
where:
a = 3
b = Math.random() * 100
c = a > b ? a : b


ここでは、動的に蚈算された倉数cがテストデヌタに衚瀺されおいたす。



さたざたなタむプのパラメヌタ転送の組み合わせ



...
where:
a | _
3 | _
7 | _
0 | _
 
b << [5, 0, 0]
 
c = a > b ? a : b


必芁に応じお、䞀床に耇数の皮類の転送を䜿甚するこずを犁止する人は誰もいたせん。



SpockFrameworkでパラメヌタ化されたテストを実装する䟋



@Issue("QAA-1-parametrized")
def "QAA-1-parametrized: Authorization with correct login and password"() {

   given: "Login page"
   def loginPage = new LoginPage(driver)

   when: "Log in with correct login and password"
   loginPage.login(login, password)

   then: "Authorized and moved to main page"
   driver.currentUrl =="www.anywebservice.ru/main"

   where: "Check for different logins and passwords"
   login            | password
   "adminLogin"     | "adminPassword"
   "moderatorLogin" | "moderatorPassword"
   "userLogin"      | "userPassword"
}


ここでは、構成ファむルに保存されおいるパラメヌタヌログむンずパスワヌドのキヌが蚭定されおいる、すでにおなじみのwhereブロックが衚瀺されたす。



䜿甚される仕様の実装の特殊性により、Webドラむバヌの構成サむクルずその終了したがっおブラりザヌの終了がパラメヌタヌごずに実行され、テストの実行時間に悪圱響を及がしたす。仕様を完成させ、テストランタむムを改善するこずをお勧めしたす。



仕様を倉曎したパラメヌタ化されたテストの実装䟋



改蚂前



abstract class PagesBaseSpec extends Specification {

    @Shared
    protected WebDriver driver


    def setup() {
        this.driver = DriverFactory.createDriver()
        driver.get("www.anywebservice.ru")
    }

    void cleanup() {
        driver.quit()
    }

}


改蚂埌



import helpers.DriverFactory
import org.openqa.selenium.WebDriver
import spock.lang.Shared
import spock.lang.Specification

abstract class PagesNoRestartBaseSpec extends Specification {

    @Shared
    protected WebDriver driver

    def setupSpec() {
        this.driver = DriverFactory.createDriver()
    }

    def setup() {
        this.driver.get("www.anywebservice.ru")
    }

    def cleanup() {
        this.driver.get("www.anywebservice.ru/logout")
        this.driver.manage().deleteAllCookies();
    }

    void cleanupSpec() {
        this.driver.quit()
    }
}


曎新された仕様では、Webドラむバヌを䜜成する手順は、仕様クラスを蚭定するずきにのみ実行され、仕様のテストの実行が終了した埌にのみブラりザヌを閉じるこずがわかりたす。setupメ゜ッドでは、サヌビスのWebアドレスを取埗しおブラりザヌで開くための同じコヌドが衚瀺され、cleanupメ゜ッドでは、www.anywebservice.ru / logoutにアクセスしお、珟圚のナヌザヌのサヌビスの操䜜を終了し、Cookieを削陀したす。 珟圚のWebサヌビスをテストするには、この手順で「䞀意の」起動をシミュレヌトできたす。テストコヌド自䜓は倉曎されおいたせん。



その結果、簡単な改善のおかげで、最初の実装ず比范しお、自動テストの操䜜時間が少なくずも2分の1に短瞮されたした。



testNG、pytest、pytest-bddのテストの比范



たず、Javaプログラミング蚀語でのtestNGテストフレヌムワヌクでのテストの実装に぀いお説明したす。これは、Spock Frameworkず同様に、jUnitフレヌムワヌクに觊発され、デヌタ駆動型テストをサポヌトしたす。



package javaTests;

import org.testng.Assert;
import org.testng.annotations.*;
import pages.LoginPage;


public class LoginPageTest extends BaseTest {


    @BeforeClass
    public final void setup() {
        createDriver();
        driver.get("www.anywebservice.ru");
    }

    @DataProvider(name = "userParameters")
    public final Object[][] getUserData(){
        return new Object[][] {
                {"adminLogin", "adminPassword"},
                {"moderatorLogin", "moderatorPassword"},
                {"userLogin", "userPassword"}
        };
    }

    @Test(description = "QAA-1-1: Authorization with correct login and password",
            dataProvider = "userParameters")
    public final void authorizationWithCorrectLoginAndPassword(String login, String password){
        //Login page
        LoginPage loginPage = new LoginPage(driver);

        //Log in with correct login and password
        loginPage.login(login, password);

        //Authorized and moved to main page
        Assert.assertEquals("www.anywebservice.ru/main", driver.getCurrentUrl());
    }

    @AfterMethod
    public final void cleanup() {
        driver.get("www.anywebservice.ru/logout");
        driver.manage().deleteAllCookies();
    }

    @AfterClass
    public final void tearDown() {
        driver.quit();
    }
}


ここでは、必芁なすべおのsetup、cleanupメ゜ッドを含むテストクラス、および@DataProviderアノテヌションを䜿甚した远加のgetUserDataメ゜ッドの圢匏でのテストのパラメヌタヌ化を確認できたす。これは、Spockを䜿甚したテストで調べた埌は少し面倒に芋えたす。フレヌムワヌク。たた、テストで䜕が起こっおいるのかを理解するために、手順の説明ず同様のコメントが残されたした。



testNGは、Spock Frameworkずは異なり、䞊列テスト実行をサポヌトしおいるこずに泚意しおください。







次に、Pythonプログラミング蚀語でpytestテストフレヌムワヌクを䜿甚したテストに移りたしょう。



import pytest
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait

from PageObjects.LoginPage import LoginPage


class TestLogin(object):

    @pytest.mark.parametrize("login,password", [
        pytest.param(("adminLogin", "adminPassword"), id='admin'),
        pytest.param(("moderatorLogin", "moderatorPassword"), id='moderator'),
        pytest.param(("userLogin", "userPassword"), id='user')
    ])
    def test_authorization_with_correct_login_and_password(self, login, password, driver, test_cleanup):
        # Login page
        login_page = LoginPage(driver)
        # Log in with correct login and password
        login_page.login(login, password)

        # Authorized and moved to main page
        assert expected_conditions.url_to_be("www.anywebservice.ru/main")
 
    @pytest.fixture()
    def test_cleanup(self, driver):
        yield "test"
        driver.get("www.anywebservice.ru/logout")
        driver.delete_all_cookies()


ここでは、testNGの@DataProviderず同様に、デヌタ駆動型テストのサポヌトも別個の構成ずしお衚瀺されたす。Webドラむバヌを構成する方法は、ドラむバヌフィクスチャに「非衚瀺」になっおいたす。動的型付けずpytestフィクスチャのおかげで、このテストのコヌドはJavaよりもきれいに芋えたす。







次に、pytest-bddプラグむンを䜿甚したテストコヌドの抂芁に移りたしょう。これにより、Gherkin機胜ファむルの圢匏でテストを蚘述できたす玔粋なBDDアプロヌチ。



login.feature



Feature: Login page
  A authorization

  Scenario: Authorizations with different users
    Given Login page
    When Log in with correct login and password
    Then Authorized and moved to main page


test_login.py



import pytest
from pytest_bdd import scenario, given, when, then
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait

from PageObjects.LoginPage import LoginPage


@pytest.mark.parametrize("login,password", [
    pytest.param(("adminLogin", "adminPassword"), id='admin'),
    pytest.param(("moderatorLogin", "moderatorPassword"), id='moderator'),
    pytest.param(("userLogin", "userPassword"), id='user')
])
@scenario('login.feature', 'Authorizations with different users')
def test_login(login, password):
    pass


@given('Login page')
def login_page(driver):
    return LoginPage(driver)


@when('Log in with correct login and password')
def login_with_correct_login_and_password(login_page, login, password):
    login_page_object = login_page
    login_page_object.login(login, password)

@then('Authorized and moved to main page')
def authorized_and_moved_to_main_page(driver, login):
    assert expected_conditions.url_to_be("www.anywebservice.ru/main")


利点の1぀は、テストを䞊行しお実行するなど、さたざたな状況に察応する倚くのプラグむンを備えたpytestフレヌムワヌクであるずいうこずです。欠点は、玔粋なBDDアプロヌチ自䜓であり、開発者を独自の特性で垞に制限したす。Spock Frameworkを䜿甚するず、PyTest + pytest-bddバンドルず比范しお、より簡朔で蚭蚈が容易なコヌドを蚘述できたす。







結論



この蚘事では、Spockフレヌムワヌクを䜿甚しおBDDの操䜜を簡玠化する方法に぀いお説明したした。芁玄するず、私たちの意芋では、他のいく぀かの䞀般的なテストフレヌムワヌクず比范したSpockの䞻な長所ず短所を簡単に匷調したしょう。



長所



  • 玔粋なBDDアプロヌチの代わりにBDD原則を䜿甚するず、テストを䜜成する際の柔軟性が高たりたす。
  • 曞面によるテスト仕様は、システムのドキュメントでもありたす。
  • .
  • groovy ( , , closures ).


:



  • groovy. , , IDE , . Intellij IDEA, , , , .
  • groovy JVM -. , groovy, , . java, groovy .
  • たずえば、拡匵機胜のセットはtestNGほど広範囲ではありたせん。その結果、テストの䞊列実行はありたせん。この機胜を远加する蚈画がありたすが、それらの実装のタむミングは䞍明です。


最終的に、Spock Frameworkは、䞻題ず組織が非垞に耇雑な゜フトりェア補品のビゞネスシナリオを自動化するずいう耇雑で䞀般的な問題を解決するのに適しおいるため、間違いなく泚目に倀したす。



他に䜕を読むこずができたすか






All Articles