لماذا نحتاج إلى فولكان على متن الطائرة: مراجعة إطار سبوك

تساعد أتمتة الاختبار على المراقبة المستمرة لجودة منتج تكنولوجيا المعلومات ، فضلاً عن تقليل التكاليف على المدى الطويل. هناك طرق مختلفة في الأتمتة ، على سبيل المثال ، التنمية المدفوعة بالسلوك (BDD) ، والتطوير من خلال السلوك.



يرتبط بهذا النهج الخيار ، وإطار عمل الروبوت ، والسلوك ، وغيرها ، والتي تفصل بين نصوص التنفيذ وتنفيذ كل بناء. يساعد هذا الفصل في كتابة نصوص قابلة للقراءة ، لكنه يستغرق وقتًا طويلاً وبالتالي قد يكون غير عملي عند كتابة تنفيذ.



دعنا نلقي نظرة على كيفية تبسيط عملك مع BDD باستخدام الأدوات المناسبة - على سبيل المثال ، إطار عمل Spock ، الذي يجمع بين الجمال والراحة لمبادئ BDD وميزات jUnit.







إطار سبوك



Spock هو إطار عمل للاختبار والمواصفات لتطبيقات Java و Groovy. باستخدام منصة JUnit كأساس ، يتوافق هذا الإطار مع جميع IDEs الشائعة (على وجه الخصوص ، IntelliJ IDEA) وأدوات البناء المختلفة (Ant و Gradle و Maven) وخوادم التكامل المستمر (CI).



كيف تكتب مطوري إطار العمل ، فإن Spock «ألهمت JUnit و RSpec و jMock و Mockito و Groovy's و Scala's و Vulcans وغيرها من أشكال الحياة المثيرة."



في هذه المقالة ، سنلقي نظرة على أحدث إصدار متاح ، Spock Framework 2.0. ميزاته: القدرة على استخدام JUnit5 و Java 8+ و groovy 2.5 (يوجد أيضًا تجميع مع الإصدار 3.0). Spock مرخص بموجب Apache 2.0 ولديه مجتمع مستخدم متجاوب. يواصل مطورو إطار العمل تحسين Spock وتطويره ، والذي يتضمن بالفعل العديد من الإضافات التي تسمح لك بضبط التشغيل التجريبي الخاص بك. على سبيل المثال ، أحد أكثر مجالات التحسين المعلنة إثارة للاهتمام هو إضافة تنفيذ الاختبار الموازي.



رائع



Groovy هي لغة برمجة موجهة للكائنات تم تطويرها لمنصة Java كإضافة مع قدرات Python و Ruby و Smalltalk. يستخدم Groovy بناء جملة يشبه Java مع تجميع ديناميكي لـ JVM bytecode ويعمل مباشرة مع أكواد Java الأخرى والمكتبات. يمكن استخدام اللغة في أي مشروع Java أو كلغة برمجة نصية.



تشمل ميزات groovy: الكتابة الثابتة والديناميكية ؛ بناء جملة مدمج للقوائم والمصفوفات والتعبيرات العادية ؛ عمليات التحميل الزائد. ومع ذلك ، ظهرت الإغلاقات في Groovy قبل وقت طويل من Java.



يعد Groovy مناسبًا تمامًا لتطوير الاختبار السريع ، حيث يمكنك استخدام السكر النحوي الشبيه بالثعبان دون القلق بشأن كتابة الكائنات.



ميزات Spock Framework



تتمثل إحدى الميزات الرئيسية لإطار العمل في أن المطور لديه القدرة على كتابة المواصفات بخصائص النظام المتوقعة باستخدام مبادئ نهج BDD. يتيح هذا النهج إمكانية تكوين اختبارات وظيفية موجهة نحو الأعمال لمنتجات البرامج ذات الموضوعات العالية والتعقيد التنظيمي.



المواصفات هي فئة رائعة تمتد spock.lang.Specification



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


يمكن أن تحتوي قائمة مكونات الصنف على حقول مساعدة مختلفة يتم تشغيلها لكل فئة قائمة مكونات الصنف.



باستخدام التعليق التوضيحي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 على المنشط ومحفز النظام ، وتحتوي الكتلة بعد ذلك على استجابة النظام.



في الحالات التي يكون فيها من الممكن تقصير جملة when-then إلى تعبير واحد ، يمكنك استخدام كتلة توقع واحدة... سيتم استخدام الأمثلة التالية من وثائق إطار عمل Spock الرسمية:



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


أو تعبير واحد



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


سيتم مناقشة أنواع نقل بيانات الإدخال أدناه.



نموذج تنفيذ الاختبار على Spock Framework



بعد ذلك ، سننظر في مناهج تنفيذ اختبار صفحة الويب للحصول على إذن المستخدم في النظام باستخدام السيلينيوم.



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


هنا نرى الفئة الأساسية لمواصفات الصفحة. في بداية الفصل ، نرى استيراد الفئات المطلوبة. ما يلي هو التعليق التوضيحي المشترك ، والذي يسمح للفئات المشتقة بالوصول إلى برنامج تشغيل الويب. في كتلة الإعداد () ، نرى رمز تهيئة محرك الويب وفتح صفحة الويب. تحتوي كتلة cleanup () على رمز إنهاء برنامج تشغيل الويب.



بعد ذلك ، دعنا ننتقل إلى نظرة عامة على مواصفات صفحة تسجيل دخول المستخدم.



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


ترث مواصفات صفحة التفويض من مواصفات الصفحة الأساسية. يحدد التعليق التوضيحي للمشكلة معرف الاختبار في نظام تتبع خارجي (مثل Jira). في السطر التالي ، نرى اسم الاختبار ، الذي يتم تعيينه ، حسب الاصطلاح ، بواسطة سلسلة حرفية ، مما يسمح باستخدام أي أحرف في اسم الاختبار (بما في ذلك الأحرف الروسية). في الكتلة المحددة ، تتم تهيئة كائن الصفحة لفئة صفحة التفويض ، ويتم الحصول على تسجيل الدخول وكلمة المرور الصحيحين للترخيص في النظام. في كتلة when ، يتم تنفيذ إجراء التفويض. يتم استخدام الكتلة ثم للتحقق من الإجراء المتوقع ، أي التخويل الناجح وإعادة التوجيه إلى الصفحة الرئيسية للنظام.



باستخدام هذه المواصفات كمثال ، نرى الميزة الأكثر أهمية لاستخدام نموذج BDD في spock - مواصفات النظام هي في نفس الوقت توثيقه. يصف كل اختبار سلوكًا معينًا ، وكل خطوة في الاختبار لها وصفها الخاص ، وهو أمر مفهوم ليس فقط للمطورين ، ولكن أيضًا للعملاء. يمكن تقديم أوصاف الكتل ليس فقط في الكود المصدري للاختبار ، ولكن أيضًا في رسائل التشخيص أو التقارير المتعلقة بعملية الاختبار.



يوفر إطار العمل القدرة على نقل العديد من عمليات تسجيل الدخول وكلمات المرور للاختبار (تحديد معلمات الاختبار).



الاختبار المستند إلى البيانات في إطار عمل Spock



الاختبار المستند إلى البيانات = الاختبار المستند إلى الجدول = الاختبار المعتمد


لاختبار سيناريو بمعلمات متعددة ، يمكنك استخدام خيارات متنوعة لتمريرها.



جداول البيانات



لنلقِ نظرة على بعض الأمثلة من وثائق إطار العمل الرسمية.



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


كل صف في الجدول عبارة عن تكرار اختبار منفصل. أيضًا ، يمكن تمثيل الجدول بعمود واحد.



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]


هنا ، التحول الأيسر << هو عامل تشغيل رائع يعمل الآن كإضافة عناصر إلى القائمة.



لكل تكرار اختبار ، سيتم طلب البيانات التالية من القائمة لكل متغير:



1 التكرار: أ = 1 ، ب = 3 ، ج = 3 ؛

التكرار الثاني: أ = 7 ، ب = 4 ، ج = 7 ؛

3 التكرار: أ = 0 ، ب = 0 ، ج = 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


لا أحد يمنعك من استخدام عدة أنواع من النقل دفعة واحدة ، إذا لزم الأمر.



مثال على تنفيذ اختبار معلمات على 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"
}


هنا نرى كتلة المكان المألوفة بالفعل ، حيث يتم تعيين مفاتيح المعلمات (تسجيلات الدخول وكلمات المرور) ، والتي يتم تخزينها في ملف التكوين.



نظرًا لخصائص التنفيذ المستخدم للمواصفات ، سيتم تنفيذ دورة تكوين برنامج تشغيل الويب وإغلاقها (وبالتالي إغلاق المتصفح) لكل معلمة ، مما سيؤثر سلبًا على وقت تنفيذ الاختبار. نقترح إنهاء المواصفات وتحسين وقت التشغيل التجريبي.



مثال على تنفيذ اختبار ذي معلمات بمواصفات معدلة



قبل المراجعة



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


في المواصفات المحدثة ، نرى أن إجراء إنشاء برنامج تشغيل ويب سيتم تنفيذه فقط عند إعداد فئة المواصفات ، وإغلاق المتصفح فقط بعد انتهاء تشغيل الاختبارات من المواصفات. في طريقة الإعداد () ، نرى نفس الرمز للحصول على عنوان الويب الخاص بالخدمة وفتحه في متصفح ، وفي طريقة التنظيف () ، نذهب إلى www.anywebservice.ru/logout لإنهاء العمل مع الخدمة للمستخدم الحالي وحذف ملفات تعريف الارتباط (لاختبار خدمة الويب الحالية ، هذا الإجراء كافٍ لمحاكاة إطلاق "فريد"). رمز الاختبار نفسه لم يتغير.



نتيجة لذلك ، بمساعدة التحسينات البسيطة ، حصلنا على الأقل على انخفاض مضاعف في وقت تشغيل الاختبار التلقائي ، مقارنة بالتنفيذ الأولي.



مقارنة الاختبارات testNG ، pytest ، pytest-bdd



أولاً ، سننظر في تنفيذ اختبار على إطار عمل اختبار testNG في لغة برمجة Java ، والتي ، مثل 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();
    }
}


هنا يمكننا أن نرى فئة الاختبار مع جميع عمليات الإعداد الضرورية () ، وطرق التنظيف () ، بالإضافة إلى تحديد معاملات الاختبار في شكل طريقة getUserData () إضافية مع التعليق التوضيحيDataProvider ، والذي يبدو مرهقًا بعض الشيء بعد ما فحصناه في الاختبار باستخدام Spock إطار العمل. أيضًا ، لفهم ما يحدث في الاختبار ، تم ترك تعليقات مشابهة لوصف الخطوات.



تجدر الإشارة إلى أن testNG ، على عكس Spock Framework ، يدعم تنفيذ الاختبار المتوازي.







بعد ذلك ، دعنا ننتقل إلى اختبار باستخدام إطار عمل اختبار pytest في لغة برمجة 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()


هنا نرى أيضًا دعمًا للاختبار المستند إلى البيانات كإنشاء منفصل ، على غرارDataProvider في testNG. طريقة تكوين برنامج تشغيل الويب "مخفية" في تركيبات برنامج التشغيل. بفضل الكتابة الديناميكية وتركيبات pytest ، يبدو هذا الاختبار أنظف من Java.







بعد ذلك ، دعنا ننتقل إلى نظرة عامة على رمز الاختبار باستخدام المكون الإضافي pytest-bdd ، والذي يسمح لك بكتابة الاختبارات في شكل ملفات ميزة Gherkin (نهج BDD النقي).



تسجيل الدخول



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


تتمثل إحدى المزايا في أنه لا يزال إطار عمل pytest ، والذي يحتوي على العديد من المكونات الإضافية لمواقف مختلفة ، بما في ذلك إجراء الاختبارات بشكل متوازٍ. الجانب السلبي هو نهج BDD النقي نفسه ، والذي سيحد باستمرار من المطور بخصائصه الخاصة. يتيح Spock Framework كتابة كود أكثر إيجازًا وسهل التصميم مقارنة بحزمة PyTest + pytest-bdd.







خاتمة



في هذه المقالة ، نظرنا في كيفية تبسيط العمل مع BDD باستخدام إطار عمل Spock. بإيجاز ، دعنا نسلط الضوء بإيجاز ، في رأينا ، على إيجابيات وسلبيات Spock مقارنة ببعض أطر الاختبار الشائعة الأخرى.



الايجابيات:



  • يمنحك استخدام مبادئ BDD بدلاً من نهج BDD النقي مزيدًا من المرونة عند كتابة الاختبارات.
  • مواصفات الاختبار المكتوبة هي أيضًا توثيق للنظام.
  • .
  • groovy ( , , closures ).


:



  • groovy. , , IDE , . Intellij IDEA, , , , .
  • groovy JVM -. , groovy, , . java, groovy .
  • مجموعة الامتدادات ليست شاملة مثل مجموعة testNG ، على سبيل المثال. نتيجة لذلك ، لا يوجد تشغيل اختباري موازٍ. هناك خطط لإضافة هذه الوظيفة ، لكن توقيت تنفيذها غير معروف.


في نهاية المطاف ، يستحق Spock Framework الاهتمام لأنه مناسب لحل المشكلة الشائعة المعقدة المتمثلة في أتمتة سيناريوهات الأعمال لمنتجات البرامج ذات الموضوعات العالية والتعقيد التنظيمي.



ماذا يمكنك أن تقرأ أيضًا:






All Articles