Mengapa Kami Membutuhkan Vulcan On Board: Spock Framework Review

Otomatisasi pengujian membantu untuk terus memantau kualitas produk TI, serta mengurangi biaya dalam jangka panjang. Ada berbagai macam pendekatan dalam otomatisasi, misalnya Behavior Driven Development (BDD), pengembangan melalui perilaku.



Terkait dengan pendekatan ini adalah ketimun, kerangka robot, perilaku, dan lain-lain, yang memisahkan skrip eksekusi dan implementasi setiap konstruk. Pemisahan ini membantu menulis skrip yang dapat dibaca, tetapi memakan waktu dan oleh karena itu dapat menjadi tidak praktis saat menulis implementasi.



Mari kita lihat bagaimana Anda dapat menyederhanakan pekerjaan Anda dengan BDD dengan menggunakan alat yang tepat - misalnya, kerangka kerja Spock, yang menggabungkan keindahan, kenyamanan prinsip BDD, dan fitur jUnit.







Kerangka spock



Spock adalah kerangka pengujian dan spesifikasi untuk aplikasi Java dan Groovy. Dengan menggunakan platform JUnit sebagai basis, framework ini kompatibel dengan semua IDE populer (khususnya, IntelliJ IDEA), berbagai fitur build (Ant, Gradle, Maven), dan server continuous integration (CI).



Bagaimana menulis pengembang kerangka kerja, Spock «menginspirasi JUnit , RSpec , jMock , Mockito , Groovy , Scala's , Vulcan dan bentuk kehidupan menarik lainnya."



Pada artikel ini, kita akan melihat versi terbaru yang tersedia, Spock Framework 2.0. Fitur-fiturnya: kemampuan untuk menggunakan JUnit5, Java 8+, groovy 2.5 (ada juga assembly dengan versi 3.0). Spock dilisensikan di bawah Apache 2.0 dan memiliki komunitas pengguna yang responsif. Pengembang kerangka kerja terus menyempurnakan dan mengembangkan Spock, yang sudah menyertakan banyak ekstensi yang memungkinkan Anda menyempurnakan uji coba Anda. Misalnya, salah satu area peningkatan paling menarik yang diumumkan adalah penambahan eksekusi uji paralel.



Groovy



Groovy adalah bahasa pemrograman berorientasi objek yang dikembangkan untuk platform Java sebagai add-on dengan kemampuan Python, Ruby, dan Smalltalk. Groovy menggunakan sintaks mirip Java dengan kompilasi dinamis ke bytecode JVM dan bekerja langsung dengan kode dan pustaka Java lainnya. Bahasa ini dapat digunakan dalam proyek Java apa pun atau sebagai bahasa skrip.



Fitur groovy meliputi: pengetikan statis dan dinamis; sintaks bawaan untuk daftar, larik, dan ekspresi reguler; kelebihan operasi. Namun, penutupan di Groovy muncul jauh sebelum Jawa.



Groovy sangat cocok untuk pengembangan pengujian cepat, di mana Anda dapat menggunakan gula sintaksis seperti python tanpa khawatir tentang mengetik objek.



Fitur Kerangka Spock



Salah satu fitur utama dari kerangka kerja ini adalah bahwa pengembang memiliki kemampuan untuk menulis spesifikasi dengan karakteristik sistem yang diharapkan menggunakan prinsip - prinsip pendekatan BDD. Pendekatan ini memungkinkan untuk menyusun tes fungsional berorientasi bisnis untuk produk perangkat lunak dengan subjek tinggi dan kompleksitas organisasi.



Spec adalah kelas yang menarik yang memperluas spock.lang.Specification



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


BOM dapat berisi berbagai bidang tambahan yang dipicu untuk setiap kelas BOM.



Dengan menggunakan anotasi @Shared, Anda dapat memberikan akses ke kolom tersebut ke kelas yang diwarisi dari spesifikasi.



abstract class PagesBaseSpec extends Specification {

    @Shared
    protected WebDriver driver


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

    void cleanup() {
        driver.quit()
    }

}


Metode kustomisasi kelas BOM:



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


Tabel berikut menunjukkan kata kunci dan metode mana dalam kerangka Spock yang memiliki rekan JUnit.







Balok adonan



Dalam Spock Framework, setiap tahap pengujian dipisahkan menjadi blok kode terpisah (lihat dokumentasi untuk contoh ).







Satu blok kode dimulai dengan label dan diakhiri dengan awal blok kode berikutnya atau akhir pengujian.



Blok yang diberikan bertanggung jawab untuk mengatur kondisi pengujian awal. Kapan , maka



balok selalu digunakan bersama. Saat blok berisi stimulan, stimulus sistem, dan blok berisi respons sistem. Dalam kasus di mana dimungkinkan untuk mempersingkat ketika-maka klausul untuk ekspresi tunggal, Anda dapat menggunakan satu mengharapkan blok



... Contoh berikut akan digunakan dari dokumentasi framework Spock resmi:



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


atau satu ekspresi



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


Blok pembersihan digunakan untuk mengosongkan resource sebelum iterasi pengujian berikutnya.



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


The mana klausul yang digunakan untuk transfer data untuk pengujian (Data Driven Testing).



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


Jenis transfer data masukan akan dibahas di bawah ini.



Contoh implementasi uji pada Spock Framework



Selanjutnya, kami akan mempertimbangkan pendekatan implementasi pengujian halaman web untuk otorisasi pengguna di sistem menggunakan selenium.



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()
    }
}


Di sini kita melihat kelas dasar spesifikasi halaman. Di awal kelas, kami melihat impor kelas yang diperlukan. Berikut ini adalah anotasi bersama , yang memungkinkan kelas turunan untuk mengakses driver web. Di blok setup () , kita melihat kode untuk menginisialisasi driver web dan membuka halaman web. Blok cleanup () berisi kode untuk menghentikan driver web.



Selanjutnya, mari beralih ke gambaran umum spesifikasi halaman login pengguna.



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"
    }
}


Spesifikasi halaman otorisasi mewarisi dari spesifikasi halaman dasar. The Issue penjelasan Menentukan ID dari tes dalam sistem pelacakan eksternal (seperti Jira). Di baris berikutnya, kita melihat nama tes, yang menurut konvensi diatur oleh literal string, yang memungkinkan Anda menggunakan karakter apa pun dalam nama tes (termasuk karakter Rusia). Di blok yang diberikan , objek halaman dari kelas halaman otorisasi diinisialisasi, dan login dan kata sandi yang benar untuk otorisasi dalam sistem diperoleh. Di blok when , tindakan otorisasi dilakukan. Blok kemudian digunakan untuk memeriksa tindakan yang diharapkan, yaitu otorisasi yang berhasil dan pengalihan ke halaman utama sistem.



Pada contoh spesifikasi ini, kami melihat nilai tambah yang paling signifikan dari penggunaan paradigma BDD di spock - spesifikasi sistem pada saat yang sama adalah dokumentasinya. Setiap pengujian mendeskripsikan perilaku tertentu, setiap langkah dalam pengujian memiliki deskripsinya sendiri, yang dapat dipahami tidak hanya untuk developer, tetapi juga untuk pelanggan. Deskripsi blok dapat disajikan tidak hanya dalam kode sumber pengujian, tetapi juga dalam pesan diagnostik atau laporan operasi pengujian.



Kerangka kerja ini menyediakan kemampuan untuk mentransfer berbagai login dan kata sandi untuk pengujian (membuat parameter pengujian).



Pengujian Berdasarkan Data dalam Kerangka Spock



Pengujian Berdasarkan Data = pengujian berdasarkan tabel = pengujian berparameter


Untuk menguji skenario dengan beberapa parameter, Anda dapat menggunakan berbagai opsi untuk meneruskannya.



Tabel Data



Mari kita lihat beberapa contoh dari dokumentasi framework resmi.



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
  }
}


Setiap baris dalam tabel adalah iterasi pengujian terpisah. Selain itu, tabel dapat diwakili oleh satu kolom.



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


_ Apakah objek rintisan dari kelas BOM.



Untuk persepsi visual yang lebih baik tentang parameter, Anda dapat menulis ulang contoh di atas dalam bentuk berikut:



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


Sekarang kita dapat melihat bahwa a , b adalah input dan c adalah nilai yang diharapkan.



Pipa data



Dalam beberapa kasus, menggunakan tabel desain akan sangat merepotkan. Dalam kasus seperti itu, Anda dapat menggunakan jenis penerusan parameter berikut:



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


Di sini, shift kiri << adalah operator groovy yang kelebihan beban yang sekarang bertindak sebagai tambahan pada daftar.



Untuk setiap iterasi pengujian, data berikut dari daftar akan diminta untuk setiap variabel:



1 iterasi: a = 1, b = 3, c = 3;

Iterasi ke-2: a = 7, b = 4, c = 7;

3 iterasi: a = 0, b = 0, c = 0.



Selain itu, data masukan tidak hanya dapat dikirim secara eksplisit, tetapi juga diminta, jika perlu, dari berbagai sumber. Misalnya, dari database:



@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")
}


Penugasan Variabel Data



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


Di sini kita melihat variabel c yang dihitung secara dinamis dalam data pengujian.



Kombinasi berbagai jenis transfer parameter



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


Tidak ada yang melarang Anda menggunakan beberapa jenis transfer sekaligus, jika perlu.



Contoh penerapan pengujian berparameter pada Spock Framework



@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"
}


Di sini kita melihat blok where yang sudah dikenal, di mana kunci parameter (login dan kata sandi) ditetapkan, yang disimpan dalam file konfigurasi.



Karena kekhasan implementasi spesifikasi yang digunakan, siklus konfigurasi driver web dan penutupannya (dan karenanya menutup browser) akan dilakukan untuk setiap parameter, yang akan berdampak negatif pada waktu eksekusi pengujian. Kami menyarankan untuk menyelesaikan spesifikasi dan meningkatkan runtime pengujian.



Contoh implementasi pengujian berparameter dengan spesifikasi yang dimodifikasi



Sebelum revisi



abstract class PagesBaseSpec extends Specification {

    @Shared
    protected WebDriver driver


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

    void cleanup() {
        driver.quit()
    }

}


Setelah revisi



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()
    }
}


Dalam spesifikasi yang diperbarui, kita melihat bahwa prosedur untuk membuat driver web akan dilakukan hanya saat menyiapkan kelas spesifikasi, dan menutup browser hanya setelah pengujian dari spesifikasi selesai dijalankan. Dalam metode setup (), kami melihat kode yang sama untuk mendapatkan alamat web dari layanan dan membukanya di browser, dan dalam metode cleanup (), kami pergi ke www.anywebservice.ru/logout untuk menyelesaikan layanan untuk pengguna saat ini dan menghapus cookie (untuk menguji layanan web saat ini, prosedur ini cukup untuk mensimulasikan peluncuran "unik"). Kode tes itu sendiri tidak berubah.



Hasilnya, dengan bantuan peningkatan sederhana, kami mendapat setidaknya dua kali lipat penurunan waktu operasi autotest, dibandingkan dengan implementasi awal.



Perbandingan tes untuk testNG, pytest, pytest-bdd



Pertama, kita akan melihat implementasi pengujian pada kerangka pengujian testNG dalam bahasa pemrograman Java, yang, seperti Spock Framework, terinspirasi oleh kerangka kerja jUnit dan mendukung pengujian berbasis data.



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();
    }
}


Di sini kita dapat melihat kelas pengujian dengan semua metode setup (), cleanup () yang diperlukan, serta parameterisasi pengujian dalam bentuk metode getUserData () tambahan dengan anotasi @DataProvider, yang terlihat agak merepotkan setelah apa yang kita periksa dalam pengujian menggunakan Spock Kerangka. Juga, untuk memahami apa yang terjadi dalam tes, komentar yang mirip dengan deskripsi langkah-langkah ditinggalkan.



Perlu dicatat bahwa testNG, tidak seperti Spock Framework, mendukung eksekusi uji paralel.







Selanjutnya, mari kita lanjutkan ke pengujian menggunakan framework pengujian pytest dalam bahasa pemrograman Python.



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()


Di sini kami juga melihat dukungan untuk pengujian berbasis data sebagai konstruksi terpisah, mirip dengan @DataProvider di testNG. Metode untuk mengkonfigurasi driver web "tersembunyi" di fixture driver. Berkat pengetikan dinamis dan perlengkapan pytest, kode untuk tes ini terlihat lebih bersih daripada Java.







Selanjutnya, mari beralih ke gambaran umum kode pengujian menggunakan plugin pytest-bdd, yang memungkinkan Anda menulis pengujian dalam bentuk file fitur Gherkin (pendekatan BDD murni).



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")


Salah satu kelebihannya adalah masih berupa framework pytest, yang memiliki banyak plugin untuk berbagai situasi, termasuk untuk menjalankan tes secara paralel. Sisi negatifnya adalah pendekatan BDD murni itu sendiri, yang secara konstan akan membatasi pengembang dengan karakteristiknya sendiri. Spock Framework memungkinkan penulisan kode yang lebih ringkas dan mudah dirancang dibandingkan dengan paket PyTest + pytest-bdd.







Kesimpulan



Pada artikel ini, kami melihat cara menyederhanakan kerja dengan BDD menggunakan kerangka kerja Spock. Sebagai kesimpulan, mari kita soroti secara singkat pro dan kontra Spock utama, menurut pendapat kami, dibandingkan dengan beberapa framework pengujian umum lainnya.



Kelebihan:



  • Menggunakan prinsip BDD daripada pendekatan BDD murni memberi Anda lebih banyak fleksibilitas saat menulis tes.
  • Spesifikasi tes tertulis juga merupakan dokumentasi dari sistem.
  • .
  • groovy ( , , closures ).


:



  • groovy. , , IDE , . Intellij IDEA, , , , .
  • groovy JVM -. , groovy, , . java, groovy .
  • Kumpulan ekstensi tidak seluas testNG, misalnya. Akibatnya, tidak ada pengujian paralel. Ada rencana untuk menambahkan fungsionalitas ini, tetapi waktu penerapannya tidak diketahui.


Pada akhirnya, Spock Framework tidak diragukan lagi layak mendapat perhatian karena cocok untuk memecahkan masalah umum yang kompleks dalam mengotomatiskan skenario bisnis untuk produk perangkat lunak dengan subjek yang tinggi dan kompleksitas organisasi.



Apa lagi yang bisa Anda baca:






All Articles