JavaRush /Kurslar /All lectures for UZ purposes /HtmlUnit Integratsiyasi

HtmlUnit Integratsiyasi

All lectures for UZ purposes
Daraja, Dars
Mavjud

Spring MockMvc va HtmlUnit orasida integratsiyani ta'minlaydi. Bu HTML asosidagi ko'rinishlardan foydalanganda skvozli testlarni amalga oshirishni osonlashtiradi. Bunday integratsiya sizga quyidagilarga imkon beradi:

  • HTML sahifalarni osonlik bilan HtmlUnit, WebDriver va Geb kabi vositalar yordamida test qilish, servlet konteynerida deploy qilishga hojat qoldirmasdan.

  • Sahifalar ichidagi JavaScript test qilish.

  • Testlashni tezlashtirish uchun imitatsiya xizmatlaridan foydalanish (ixtiyoriy).

  • Logika skvozli testlar va konteynerdan tashqaridagi integratsiya testlari o'rtasida bo'lishiladi.

MockMvc servlet konteyneriga bog'liq bo'lmagan shablon texnologiyalarida ishlaydi (masalan, Thymeleaf, FreeMarker va boshqalar), lekin u JSP texnologiyasi bilan ishlamaydi, chunki u servlet konteyneriga bog'liq.

Nima uchun HtmlUnit integratsiyasi kerak?

Aqlga keladigan eng aniq savol: "Nima uchun bu kerak?". Javobni eng oson yo'li, bir nechta qo'llanma asosidagi ilovani o'rganishdir. Tasavvur qiling, sizda Spring MVC ilovasi bor, u Message ob'ekti uchun CRUD operatsiyalarini qo'llab-quvvatlaydi. Ilova barcha xabarlarni sahifalashni ham qo'llab-quvvatlaydi. Uni qanday test qilish mumkin?

Spring MVC Test yordamida biz Message ni yaratib bilamizmi deganini osongina tekshirib olishimiz mumkin, quyidagi tarzda:

Java
MockHttpServletRequestBuilder createMessage = post("/messages/")
        .param("summary", "Spring Rocks")
        .param("text", "In case you didn't know, Spring Rocks!");
mockMvc.perform(createMessage)
        .andExpect(status().is3xxRedirection())
        .andExpect(redirectedUrl("/messages/123"));
Kotlin
@Test
fun test() {
    mockMvc.post("/messages/") {
        param("summary", "Spring Rocks")
        param("text", "In case you didn't know, Spring Rocks!")
    }.andExpect {
        status().is3xxRedirection()
        redirectedUrl("/messages/123")
    }
}

Agar bizda xabar yaratishga imkon beradigan forma ko'rinishini test qilish kerak bo'lsa-chi? Masalan, formamiz quyidagi parchaga o'xshasa:

<form id="messageForm" action="/messages/" method="post">
    <div class="pull-right"><a href="/messages/">Messages</a></div>
    <label for="summary">Summary</label>
    <input type="text" class="required" id="summary" name="summary" value="" />
    <label for="text">Message</label>
    <textarea id="text" name="text"></textarea>
    <div class="form-actions">
        <input type="submit" value="Create" />
    </div>
</form>

Qanday qilib formamiz yangi xabar yaratish uchun to'g'ri so'rov yuboradi? Naive urinish quyidagicha ko'rinishga ega bo'lishi mumkin:

Java
mockMvc.perform(get("/messages/form"))
        .andExpect(xpath("//input[@name='summary']").exists())
        .andExpect(xpath("//textarea[@name='text']").exists());
Kotlin
mockMvc.get("/messages/form").andExpect {
    xpath("//input[@name='summary']") { exists() }
    xpath("//textarea[@name='text']") { exists() }
}

Ushbu testning bir nechta aniq kamchiliklari bor. Agar biz controllerimizni text o'rniga message parametridan foydalanishga yangilasak, formani sinovdan o'tkazamiz, hatto HTML forma controller bilan sinxronizatsiyalanmasa ham. Ushbu muammoni hal qilish uchun biz ikki testimizni quyidagicha birlashtirishimiz mumkin:

Java
String summaryParamName = "summary";
String textParamName = "text";
mockMvc.perform(get("/messages/form"))
        .andExpect(xpath("//input[@name='" + summaryParamName + "']").exists())
        .andExpect(xpath("//textarea[@name='" + textParamName + "']").exists());
MockHttpServletRequestBuilder createMessage = post("/messages/")
        .param(summaryParamName, "Spring Rocks")
        .param(textParamName, "In case you didn't know, Spring Rocks!");
mockMvc.perform(createMessage)
        .andExpect(status().is3xxRedirection())
        .andExpect(redirectedUrl("/messages/123"));
Kotlin
val summaryParamName = "summary";
val textParamName = "text";
mockMvc.get("/messages/form").andExpect {
    xpath("//input[@name='$summaryParamName']") { exists() }
    xpath("//textarea[@name='$textParamName']") { exists() }
}
mockMvc.post("/messages/") {
    param(summaryParamName, "Spring Rocks")
    param(textParamName, "In case you didn't know, Spring Rocks!")
}.andExpect {
    status().is3xxRedirection()
    redirectedUrl("/messages/123")
}

Bu bizning testimizni noto'g'ri o'tkazish xavfini kamaytiradi, lekin hali ham ba'zi muammolar qolmoqda:

  • Sahifada bir nechta forma bo'lsa nima qilish kerak? Albatta, biz XPath ifodalarini yangilashimiz mumkin, lekin ular yanada murakkablashadi, chunki biz ko'proq omillarni hisobga olamiz: Maydonlar to'g'ri turdami? Maydonlar faollashtirilganmi? Va hokazo.

  • Boshqa muammo, biz kutilganidan ikki baravar ko'p ish qilamiz. Avvalo ko'rinishni tekshirishimiz kerak, keyin esa ko'rinishni yangi tekshirilgan parametrlar bilan yuborishimiz kerak. Ideal holatda, bularning barchasini bir vaqtning o'zida bajarish mumkin.

  • Nihoyat, ba'zi narsalarni hisobga olib bo'lmaydi. Masalan, agar forma JavaScript validatsiyasiga ega bo'lsa, biz ham uni test qilmoqchi bo'lsak nima bo'ladi?

Umumiy muammo shundaki, veb-sahifani test qilish bir martalik interaktivlikni nazarda tutmaydi. Aksincha, bu foydalanuvchi veb-sahifa bilan qanday o'zaro aloqada bo'lsa, sahifa ham boshqa resurslar bilan qanday o'zaro aloqada bo'lishining kombinatsiyasi. Masalan, forma ko'rinishi foydalanuvchi uchun xabar yaratishda kirish ma'lumotlari sifatida ishlatiladi. Shuningdek, forma ko'rinishi potentsial sahifa ishini ta'sir etuvchi qo'shimcha resurslarni, masalan, JavaScript validatsiyasini ishlatishi mumkin.

Integratsiya testlarini yordamga chaqirish?

Yuqorida keltirilgan muammolarni hal qilish uchun skvozli integratsiya testini o'rganish mumkin, lekin u bir qator kamayish tomonlariga ega. Xabarlarni ko'rish imkonini beradigan ko'rinishni test qilishni ko'rib chiqaylik. Quyidagini test qilishimiz kerak bo'lishi mumkin:

  • Agar xabarlar bo'sh bo'lsa, sahifamiz foydalanuvchiga natijalar yo'qligini bildiradimi?

  • Sahifamiz bir alohida xabarni to'g'ri ko'rsatadimi?

  • Sahifamiz sahifalashni (paginatsiyani) to'g'ri qo'llab-quvvatlaydimi?

Ushbu testlarni sozlash uchun, bizning ma'lumotlar bazamiz kerakli xabarlarni o'z ichiga olishi kerakligiga ishonch hosil qilish kerak. Bu bir qator qo'shimcha muammolarni keltirib chiqaradi:

  • Ma'lumotlar bazasida kerakli xabarlarning mavjudligi bilan ta'minlash mehnat talab qiluvchi jarayon bo'lishi mumkin. (Chetki kalit cheklovlarini hisobga olgan holda).

  • Testlash sekinlashishi mumkin, chunki har bir test ma'lumotlar bazasining to'g'ri holatda ekanligini tekshirish kerak.

  • Ma'lumotlar bazasi muayyan holatda bo'lishi kerakligi sababli testlarni parallel ishlatib bo'lmaydi.

  • Avtomatik ravishda yaratiladigan identifikatorlar, vaqt belgilari va boshqalar kabi elementlar uchun tasdiqlarni amalga oshirish qiyin bo'lishi mumkin.

Bu muammolar skvozli integratsiya testlaridan to'liq voz kechish kerakligini anglatmaydi. Buning o'rniga biz detallashtirilgan testlarimizni qayta tashkil qilib, ancha tezroq, ishonchli va hech qanday salbiy ta'sir ko'rsatmaydigan imitatsiya xizmatlaridan foydalanamiz. Keyin bir nechta haqiqiy skvozli integratsiya testlari bajarilib, oddiy ish jarayonlarini tekshirib ko'rish mumkin va barchasi to'g'ri ishlashiga ishonch hosil qilinadi.

HtmlUnit integratsiyasini joriy qilish

Sahifalarimizning o'zaro aloqasini test qilish va test to'plamimizda to'g'ri unumdorlikni saqlab qolish o'rtasida balansni qanday topish mumkin? Javob: "MockMvcni HtmlUnit bilan integratsiya qilish orqali".

HtmlUnit integratsiyasi parametrlar

Agar MockMvcni HtmlUnit bilan integratsiya qilish kerak bo'lsa, bir nechta variantlar mavjud:

  • MockMvc va HtmlUnit: Agar HtmlUnitning xom kutubxonalaridan foydalanmoqchi bo'lsangiz, ushbu variantni ishlating.

  • MockMvc va WebDriver: Integratsiya va skvozli testlar orasidagi kodni qayta ishlatishni va oson rivojlantirishni xohlasangiz, ushbu variantni ishlating.

  • MockMvc va Geb: Agar testlash uchun Groovy'dan foydalanmoqchi bo'lsangiz va integratsiya va skvozli testlar orasidagi kodni qayta ishlatishni xohlasangiz, ushbu variantni ishlating.

MockMvc va HtmlUnit

Ushbu bo'limda MockMvc va HtmlUnitni qanday integratsiya qilishni tasvirlangan. Agar siz HtmlUnitning xom kutubxonalaridan foydalanmoqchi bo'lsangiz, ushbu variantni ishlating.

MockMvc va HtmlUnitni sozlash

Birinchi navbatda, net.sourceforge.htmlunit:htmlunit test bog'langanligini yoqganingizga ishonch hosil qiling. Apache HttpComponents 4.5+ bilan HtmlUnitni ishlatish uchun HtmlUnit 2.18 yoki yuqoriroq versiyalarini ishlatishingiz kerak.

MockMvc bilan integratsiyalangan HtmlUnitdan WebClient ni MockMvcWebClientBuilder yordamida osonlik bilan yaratish mumkin, quyida ko'rsatilganidek:

Java
WebClient webClient;
@BeforeEach
void setup(WebApplicationContext context) {
    webClient = MockMvcWebClientBuilder
            .webAppContextSetup(context)
            .build();
}
Kotlin
lateinit var webClient: WebClient
@BeforeEach
fun setup(context: WebApplicationContext) {
    webClient = MockMvcWebClientBuilder
            .webAppContextSetup(context)
            .build()
}
Bu MockMvcWebClientBuilder dan foydalanishning oddiy misoli. Kengaytirilgan foydalanish bo'yicha batafsil ma'lumot uchun "MockMvcWebClientBuilder kengaytirilgan foydalanish" bo'limiga qarang.

Shunday qilib, localhostni server sifatida ishlatadigan har qanday URL-manzilni haqiqiy HTTP ulanishisiz bevosita bizning MockMvc instancesiga yo'naltirilishi mumkin. Har qanday boshqa URL-manzil so'rash odatdagidek tarmoq aloqasi orqali amalga oshiriladi. Bu bizga kontent yetkazib berish tarmoqlari (CDN)dan foydalanishni osonlik bilan test qilish imkoniyatini beradi.

MockMvc va HtmlUnitni ishlatish

Endi biz HtmlUnitni odatdagidek ishlatishimiz mumkin, lekin ilovamizni servlet konteyneriga joylashtirishga hojat qolmaydi. Masalan, xabar yaratish ko'rinishini quyidagicha so'rash mumkin:

Java
HtmlPage createMsgFormPage = webClient.getPage("http://localhost/messages/form");
Kotlin
val createMsgFormPage = webClient.getPage("http://localhost/messages/form")
Kontekst yo'li odatda "" ga teng. Bundan tashqari, kontekst yo'lini "MockMvcWebClientBuilder kengaytirilgan foydalanish" bo'limida ko'rsatilganidek belgilashingiz mumkin.

HtmlPagega havola olgandan so'ng, quyidagi misolda ko'rsatilganidek formani to'ldirish va yuborish orqali xabar yaratish mumkin:

Java
HtmlForm form = createMsgFormPage.getHtmlElementById("messageForm");
HtmlTextInput summaryInput = createMsgFormPage.getHtmlElementById("summary");
summaryInput.setValueAttribute("Spring Rocks");
HtmlTextArea textInput = createMsgFormPage.getHtmlElementById("text");
textInput.setText("In case you didn't know, Spring Rocks!");
HtmlSubmitInput submit = form.getOneHtmlElementByAttribute("input", "type", "submit");
HtmlPage newMessagePage = submit.click();
Kotlin
val form = createMsgFormPage.getHtmlElementById("messageForm")
val summaryInput = createMsgFormPage.getHtmlElementById("summary")
summaryInput.setValueAttribute("Spring Rocks")
val textInput = createMsgFormPage.getHtmlElementById("text")
textInput.setText("In case you didn't know, Spring Rocks!")
val submit = form.getOneHtmlElementByAttribute("input", "type", "submit")
val newMessagePage = submit.click()

Nihoyat, yangi xabarning muvaffaqiyatli yaratilganiga ishonch hosil qilish mumkin. Quyidagi tasdiqlar AssertJ kutubxonasidan foydalanadi:

Java
assertThat(newMessagePage.getUrl().toString()).endsWith("/messages/123");
String id = newMessagePage.getHtmlElementById("id").getTextContent();
assertThat(id).isEqualTo("123");
String summary = newMessagePage.getHtmlElementById("summary").getTextContent();
assertThat(summary).isEqualTo("Spring Rocks");
String text = newMessagePage.getHtmlElementById("text").getTextContent();
assertThat(text).isEqualTo("In case you didn't know, Spring Rocks!");
Kotlin
assertThat(newMessagePage.getUrl().toString()).endsWith("/messages/123")
val id = newMessagePage.getHtmlElementById("id").getTextContent()
assertThat(id).isEqualTo("123")
val summary = newMessagePage.getHtmlElementById("summary").getTextContent()
assertThat(summary).isEqualTo("Spring Rocks")
val text = newMessagePage.getHtmlElementById("text").getTextContent()
assertThat(text).isEqualTo("In case you didn't know, Spring Rocks!")

Oldingi kod bizning MockMvc testimizni bir necha yo'llar bilan yaxshilaydi. Birinchidan, biz formamizni aniq tekshirish kerak emas, keyin esa forma kabi ko'rinishni yaratish kerak emas. Buning o'rniga, formani so'rab, uni to'ldirib, yuborib, jarayonning kechikishini sezilarli darajada kamaytiramiz.

Muhim omil shundaki, HtmlUnit Mozilla Rhino dvigatelidan JavaScript logikasini hisoblash uchun foydalanadi. Bu shuni anglatadiki, biz sahifalarimizdagi JavaScript logikasini ham test qilishimiz mumkin.

HtmlUnitdan foydalanish haqida qo'shimcha ma'lumot olish uchun HtmlUnit foydalanish bo'yicha dokumentatsiyasiga qarang.

Kengaytirilgan MockMvcWebClientBuilder dan foydalanish

Hozirgacha biz MockMvcWebClientBuilder dan eng oddiy usulda foydalanib, Spring TestContext Framework tomonidan biz uchun yuklangan WebApplicationContext asosida WebClient yaratdik. Ushbu yondashuv quyidagi misolda keltirilgan:

Java
WebClient webClient;
@BeforeEach
void setup(WebApplicationContext context) {
    webClient = MockMvcWebClientBuilder
            .webAppContextSetup(context)
            .build();
}
Kotlin
lateinit var webClient: WebClient
@BeforeEach
fun setup(context: WebApplicationContext) {
    webClient = MockMvcWebClientBuilder
            .webAppContextSetup(context)
            .build()
}

Bundan tashqari, quyidagi misolda ko'rsatilganidek qo'shimcha konfiguratsiya parametrlarini ko'rsatish mumkin:

Java
WebClient webClient;
@BeforeEach
void setup() {
    webClient = MockMvcWebClientBuilder
        // MockMvcConfigurer (Spring Security)ni qo'llashni namoyish etadi
        .webAppContextSetup(context, springSecurity())
        // faqat misol uchun – odatda ""
        .contextPath("")
        // Odatda MockMvc faqat localhost uchun ishlatiladi;
        // quyidagi qism example.com va example.org uchun ham MockMvcdan foydalanadi
        .useMockMvcForHosts("example.com","example.org")
        .build();
}
Kotlin
lateinit var webClient: WebClient
@BeforeEach
fun setup() {
    webClient = MockMvcWebClientBuilder
        // MockMvcConfigurer (Spring Security)ni qo'llashni namoyish etadi
        .webAppContextSetup(context, springSecurity())
        // faqat misol uchun – odatda ""
        .contextPath("")
        // Odatda MockMvc faqat localhost uchun ishlatiladi;
        // quyidagi qism example.com va example.org uchun ham MockMvcdan foydalanadi
        .useMockMvcForHosts("example.com","example.org")
        .build()
}

Yoki, MockMvc ni alohida konfiguratsiya qilish va uni MockMvcWebClientBuilder ga quyidagi tarzda o'tkazish orqali bir xil konfiguratsiyani bajarish mumkin:

Java
MockMvc mockMvc = MockMvcBuilders
        .webAppContextSetup(context)
        .apply(springSecurity())
        .build();
webClient = MockMvcWebClientBuilder
        .mockMvcSetup(mockMvc)
        // faqat misol uchun – odatda ""
        .contextPath("")
        // Odatda MockMvc faqat localhost uchun ishlatiladi;
        // quyidagi qism example.com va example.org uchun ham MockMvcdan foydalanadi
        .useMockMvcForHosts("example.com","example.org")
        .build();
Kotlin
// Kotlin tilida imkonsiz, https://youtrack.jetbrains.com/issue/KT-22208 hal bo'lmaguncha

Bu yanada yuklangan kod, lekin MockMvc instancesi yordamida WebClient yaratib, biz MockMvcning barcha funktsiyalaridan foydalanishimiz mumkin.

MockMvc instancesini yaratish haqida qo'shimcha ma'lumotni "Sozlash Parametrlari" bo'limida topishingiz mumkin.

MockMvc va WebDriver

Oldingi bo'limlarda biz MockMvcni HtmlUnitning xom API'lari bilan ishlatishni ko'rib chiqdik. Bu bo'limda biz Seleniumning WebDriver dan foydalanib, haqiqiy kodni yanada osonlashtiruvchi qo'shimcha abstraktsiyalardan foydalanamiz.

Nima uchun WebDriver va MockMvc?

Biz allaqachon HtmlUnit va MockMvcni ishlatishimiz mumkin, shunday bo'lsa-da, WebDriverga nima ehtiyoj bor? Selenium WebDriver juda chiroyli API-interfeysini taqdim etadi, bu bizga kodni osongina tashkil qilishimizga yordam beradi. Bu qanday ishlashini yaxshiroq tushunish uchun biz misolni ko'rib chiqamiz.

Garchi WebDriver Seleniumning bir qismi bo'lsa-da, testlaringizni ishga tushirish uchun Selenium Server kerak emas.

Xabar to'g'ri yaratilganiga ishonch hosil qilishimiz kerakligini tasavvur qiling. Testlar bir qator HTML forma elementlarini topish, ularni to'ldirish va turli xil tasdiqlarni amalga oshirishni o'z ichiga oladi.

Bunday yondashuv ko'plab alohida testlarni keltirib chiqaradi, chunki biz xatolar yuzaga keladigan shartlarni ham tekshirishimiz kerak. Masalan, biz formaning faqat qismiga to'g'ri kirganimizda xato berilishini xohlaymiz. Agar biz butun formani to'ldirsak, yangi yaratilgan xabar ko'rinishi kerak.

Agar bir maydon "summary" nomli bo'lsa, biz bir necha joylarda takrorlanadigan quyidagi narsaga o'xshash narsani olishimiz mumkin:

Java
HtmlTextInput summaryInput = currentPage.getHtmlElementById("summary");
summaryInput.setValueAttribute(summary);
Kotlin
val summaryInput = currentPage.getHtmlElementById("summary")
summaryInput.setValueAttribute(summary)

Agar biz id ni smmry ga o'zgartirsak nima bo'ladi? Bu bizni testlarimizning barchasini yangilashga majbur qiladi, bu o'zgarishni hisobga olgan holda. Bu DRY ("O'zingizni takrorlamang") printsipini buzadi, shuning uchun biz ushbu kodni alohida metodga ajratishimiz kerak, quyidagidek:

Java
public HtmlPage createMessage(HtmlPage currentPage, String summary, String text) {
    setSummary(currentPage, summary);
    // ...
}
public void setSummary(HtmlPage currentPage, String summary) {
    HtmlTextInput summaryInput = currentPage.getHtmlElementById("summary");
    summaryInput.setValueAttribute(summary);
}
Kotlin
fun createMessage(currentPage: HtmlPage, summary:String, text:String) :HtmlPage{
    setSummary(currentPage, summary);
    // ...
}
fun setSummary(currentPage:HtmlPage , summary: String) {
    val summaryInput = currentPage.getHtmlElementById("summary")
    summaryInput.setValueAttribute(summary)
}

Bu bizga foydalanuvchi interfeysini o'zgartirsak, barcha testlarni yangilash kerak emasligiga kafolat beradi.

Biz hatto buni yanada rivojlantirib, hozirda HtmlPageda joylashgan Object ichiga joylashtirishimiz mumkin, quyidagi misolda ko'rsatilganidek:

Java
public class CreateMessagePage {
    final HtmlPage currentPage;
    final HtmlTextInput summaryInput;
    final HtmlSubmitInput submit;
    public CreateMessagePage(HtmlPage currentPage) {
        this.currentPage = currentPage;
        this.summaryInput = currentPage.getHtmlElementById("summary");
        this.submit = currentPage.getHtmlElementById("submit");
    }
    public <T> T createMessage(String summary, String text) throws Exception {
        setSummary(summary);
        HtmlPage result = submit.click();
        boolean error = CreateMessagePage.at(result);
        return (T) (error ? new CreateMessagePage(result) : new ViewMessagePage(result));
    }
    public void setSummary(String summary) throws Exception {
        summaryInput.setValueAttribute(summary);
    }
    public static boolean at(HtmlPage page) {
        return "Create Message".equals(page.getTitleText());
    }
}
Kotlin
    class CreateMessagePage(private val currentPage: HtmlPage) {
        val summaryInput: HtmlTextInput = currentPage.getHtmlElementById("summary")
        val submit: HtmlSubmitInput = currentPage.getHtmlElementById("submit")
        fun <T> createMessage(summary: String, text: String): T {
            setSummary(summary)
            val result = submit.click()
            val error = at(result)
            return (if (error) CreateMessagePage(result) else ViewMessagePage(result)) as T
        }
        fun setSummary(summary: String) {
            summaryInput.setValueAttribute(summary)
        }
        fun at(page: HtmlPage): Boolean {
            return "Create Message" == page.getTitleText()
        }
    }
}

Bu yorliqlar oldin Sahifa Ob'ekt Shaypioni (Page Object Pattern) nomi bilan ma'lum edi. Albatta, bu HtmlUnit bilan qilish mumkin, ammo WebDriver ushbu noma'lumini amalga oshirishda ancha oson va biz buni keyingi bo'limlarda ko'rib chiqamiz.

MockMvc va WebDriverni sozlash

Selenium WebDriverni Spring MVC Test freymvorki bilan ishlatish uchun, loyihangizda org.seleniumhq.selenium:selenium-htmlunit-driver test bog'langanligini tekshiring.

MockMvc bilan integratsiyalangan Selenium WebDriverni MockMvcHtmlUnitDriverBuilder yordamida osongina yaratish mumkin, quyidagi misolda ko'ring:

Java
WebDriver driver;
@BeforeEach
void setup(WebApplicationContext context) {
    driver = MockMvcHtmlUnitDriverBuilder
            .webAppContextSetup(context)
            .build();
}
Kotlin
lateinit var driver: WebDriver
@BeforeEach
fun setup(context: WebApplicationContext) {
    driver = MockMvcHtmlUnitDriverBuilder
            .webAppContextSetup(context)
            .build()
}
Bu MockMvcHtmlUnitDriverBuilder dan foydalanishning oddiy misoli. Kengaytirilgan foydalanish parametrlarini "MockMvcHtmlUnitDriverBuilder kengaytirilgan foydalanish" bo'limida ko'rishingiz mumkin.

Oldingi misolda localhostni server sifatida ishlatadigan har qanday URL-manzilni haqiqiy HTTP ulanishisiz bevosita bizning MockMvc instancesiga yo'naltirilishi kafolatlanadi. Har qanday boshqa URL-manzil so'rash odatdagidek tarmoq aloqasi orqali amalga oshiriladi. Bu bizga kontent yetkazib berish tarmoqlari (CDN)dan foydalanishni test qilishni osonlashtiradi.

MockMvc va WebDriverni ishlatish

Endi biz WebDriverni odatdagidek ishlatishimiz mumkin, lekin ilovamizni servlet konteyneriga joylashtirishga hojat qolmaydi. Masalan, xabar yaratish uchun quyidagi ko'rinishni so'rash mumkin:

Java
CreateMessagePage page = CreateMessagePage.to(driver);
Kotlin
val page = CreateMessagePage.to(driver)

Keyinchalik formani to'ldirib, quyidagi usulda yuborish orqali xabar yaratish mumkin:

Java
ViewMessagePage viewMessagePage =
        page.createMessage(ViewMessagePage.class, expectedSummary, expectedText);
Kotlin
val viewMessagePage =
    page.createMessage(ViewMessagePage::class, expectedSummary, expectedText)

Bu bizning HtmlUnit testimizni Sahifa Ob'ekt Shaypioni (Page Object Pattern)ni ishlatish orqali ancha yaxshilaydi. Ilgari "Nima uchun WebDriver va MockMvc?" bobida aytib o'tganimizdek, HtmlUnit bilan Sahifa Ob'ekt Shaypioni ni ishlatish mumkin, lekin WebDriverda bu yanada oson. Bizning CreateMessagePagening quyidagi amalga oshirilishi ko'rib chiqamiz:

Java
public class CreateMessagePage
        extends AbstractPage { 
    
    private WebElement summary;
    private WebElement text;
    
    @FindBy(css = "input[type=submit]")
    private WebElement submit;
    public CreateMessagePage(WebDriver driver) {
        super(driver);
    }
    public <T> T createMessage(Class<T> resultPage, String summary, String details) {
        this.summary.sendKeys(summary);
        this.text.sendKeys(details);
        this.submit.click();
        return PageFactory.initElements(driver, resultPage);
    }
    public static CreateMessagePage to(WebDriver driver) {
        driver.get("http://localhost:9990/mail/messages/form");
        return PageFactory.initElements(driver, CreateMessagePage.class);
    }
}
  1. CreateMessagePage AbstractPageni kengaytiradi. AbstractPage bilan batafsil tanishmaylik, lekin qisqacha aytganda, u barcha sahifalarimiz uchun bir xil funksionallikni o'z ichiga oladi. Masalan, agar bizning ilovamizda navigatsiya paneli, global xatolik xabarlari va boshqa funksiyalar bo'lsa, bu mantiqni umumiy joyga qo'yishimiz mumkin.
  2. Bizda har bir qiziqarli qism uchun WebElement turiga ega a'zolar mavjud. WebDriverning PageFactory dan foydalanib, HtmlUnit versiyasidagi CreateMessagePagelardagi ortiqcha kodni osongina olib tashlaymiz, chunki har bir WebElement avtomatik ravishda hal qilindi. PageFactory#initElements(WebDriver,Class<T>) metodi har bir WebElementni avtomatik ravishda hal qiladi, maydon nomi bo'yicha yuqoriga qarab, HTML sahifasidagi id yoki name elementini qidiradi.
  3. Biz annotatsiya @FindBy dan foydalanib, standart qidirish mantiqini o'zgartirishimiz mumkin. Bizning misolda, css selector (masalan, input[type=submit]) yordamida yuborish tugmasini qidirish uchun @FindBy annotatsiyasidan qanday foydalanish ko'rsatilgan.
Kotlin
class CreateMessagePage(private val driver: WebDriver) : AbstractPage(driver) { 
    
    private lateinit var summary: WebElement
    private lateinit var text: WebElement
    
    @FindBy(css = "input[type=submit]")
    private lateinit var submit: WebElement
    fun <T> createMessage(resultPage: Class<T>, summary: String, details: String): T {
        this.summary.sendKeys(summary)
        text.sendKeys(details)
        submit.click()
        return PageFactory.initElements(driver, resultPage)
    }
    companion object {
        fun to(driver: WebDriver): CreateMessagePage {
            driver.get("http://localhost:9990/mail/messages/form")
            return PageFactory.initElements(driver, CreateMessagePage::class.java)
        }
    }
}
  1. CreateMessagePage AbstractPageni kengaytiradi. AbstractPage bilan batafsil tanishmaylik, lekin qisqacha aytganda, u barcha sahifalarimiz uchun bir xil funksionallikni o'z ichiga oladi. Masalan, agar bizning ilovamizda navigatsiya paneli, global xatolik xabarlari va boshqa funksiyalar bo'lsa, bu mantiqni umumiy joyga qo'yishimiz mumkin.
  2. Bizda har bir qiziqarli qism uchun WebElement turiga ega a'zolar mavjud. WebDriverning PageFactory dan foydalanib, HtmlUnit versiyasidagi CreateMessagePagelardagi ortiqcha kodni osongina olib tashlaymiz, chunki har bir WebElement avtomatik ravishda hal qilindi. PageFactory#initElements(WebDriver,Class<T>) metodi har bir WebElementni avtomatik ravishda hal qiladi, maydon nomi bo'yicha yuqoriga qarab, HTML sahifasidagi id yoki name elementini qidiradi.
  3. Biz annotatsiya @FindBy dan foydalanib, standart qidirish mantiqini o'zgartirishimiz mumkin. Bizning misolda, css selector (masalan, input[type=submit]) yordamida yuborish tugmasini qidirish uchun @FindBy annotatsiyasidan qanday foydalanish ko'rsatilgan.

Nihoyat, yangi xabarning muvaffaqiyatli yaratilganiga ishonch hosil qilish mumkin. Quyidagi tasdiqlar AssertJ tasdiqlash kutubxonasidan foydalanadi:

Java
assertThat(viewMessagePage.getMessage()).isEqualTo(expectedMessage);
assertThat(viewMessagePage.getSuccess()).isEqualTo("Successfully created a new message");
Kotlin
assertThat(viewMessagePage.message).isEqualTo(expectedMessage)
assertThat(viewMessagePage.success).isEqualTo("Successfully created a new message")

Ko'rdikki, bizning ViewMessagePage sahifamiz bizga o'zimizning maxsus domenga bog'langan model bilan ishlash imkoniyatini beradi. Masalan, u Message ob'ektini qaytaruvchi metodni ochadi:

Java
public Message getMessage() throws ParseException {
    Message message = new Message();
    message.setId(getId());
    message.setCreated(getCreated());
    message.setSummary(getSummary());
    message.setText(getText());
    return message;
}
Kotlin
fun getMessage() = Message(getId(), getCreated(), getSummary(), getText())

Keyin biz o'zimizning tasdiqlarimizda to'liq funktsional domen ob'ektlaridan foydalanishimiz mumkin.

Nihoyat, test yakunlangandan so'ng WebDriver instancesini yopishni unutmaslik kerak, quyidagi tarzda:

Java
@AfterEach
void destroy() {
    if (driver != null) {
        driver.close();
    }
}
Kotlin
@AfterEach
fun destroy() {
    if (driver != null) {
        driver.close()
    }
}

WebDriverdan foydalanish haqida qo'shimcha ma'lumotni Selenium WebDriver dokumentatsiyasida topishingiz mumkin.

Kengaytirilgan MockMvcHtmlUnitDriverBuilder dan foydalanish

Hozirgacha biz MockMvcHtmlUnitDriverBuilder dan eng oddiy usulda foydalanib, Spring TestContext Framework tomonidan biz uchun yuklangan WebApplicationContext asosida WebDriver yaratdik. Ushbu yondashuv quyida takrorlanmoqda:

Java
WebDriver driver;
@BeforeEach
void setup(WebApplicationContext context) {
    driver = MockMvcHtmlUnitDriverBuilder
            .webAppContextSetup(context)
            .build();
}
Kotlin
lateinit var driver: WebDriver
@BeforeEach
fun setup(context: WebApplicationContext) {
    driver = MockMvcHtmlUnitDriverBuilder
            .webAppContextSetup(context)
            .build()
}

Bundan tashqari, quyida keltirilganidek qo'shimcha konfiguratsiya parametrlarini ko'rsatish mumkin:

Java
WebDriver driver;
@BeforeEach
void setup() {
    driver = MockMvcHtmlUnitDriverBuilder
            // MockMvcConfigurer (Spring Security)ni qo'llashni namoyish etadi
            .webAppContextSetup(context, springSecurity())
            // faqat misol uchun – odatda ""
            .contextPath("")
            // Odatda MockMvc faqat localhost uchun ishlatiladi;
            // quyidagi qism example.com va example.org uchun ham MockMvcdan foydalanadi
            .useMockMvcForHosts("example.com","example.org")
            .build();
}
Kotlin
lateinit var driver: WebDriver
@BeforeEach
fun setup() {
    driver = MockMvcHtmlUnitDriverBuilder
            // MockMvcConfigurer (Spring Security)ni qo'llashni namoyish etadi
            .webAppContextSetup(context, springSecurity())
            // faqat misol uchun – odatda ""
            .contextPath("")
            // Odatda MockMvc faqat localhost uchun ishlatiladi;
            // quyidagi qism example.com va example.org uchun ham MockMvcdan foydalanadi
            .useMockMvcForHosts("example.com","example.org")
            .build()
}

Yoki, MockMvc ni alohida konfiguratsiya qilish va uni MockMvcHtmlUnitDriverBuilder ga quyidagi tarzda o'tkazish orqali bir xil konfiguratsiyani bajarish mumkin:

Java
MockMvc mockMvc = MockMvcBuilders
        .webAppContextSetup(context)
        .apply(springSecurity())
        .build();
driver = MockMvcHtmlUnitDriverBuilder
        .mockMvcSetup(mockMvc)
        // faqat misol uchun – odatda ""
        .contextPath("")
        // Odatda MockMvc faqat localhost uchun ishlatiladi;
        // quyidagi qism example.com va example.org uchun ham MockMvcdan foydalanadi
        .useMockMvcForHosts("example.com","example.org")
        .build();
Kotlin
// Kotlin tilida imkonsiz, https://youtrack.jetbrains.com/issue/KT-22208 hal bo'lmaguncha

Bu yanada yuklangan kod, lekin MockMvc instancesi yordamida WebDriver yaratib, biz MockMvcning barcha funktsiyalaridan foydalanishimiz mumkin.

MockMvc instancesini yaratish haqida qo'shimcha ma'lumotni "Sozlash Parametrlari" bo'limida topishingiz mumkin.

MockMvc va Geb

Oldingi bo'limda, biz MockMvcni WebDriver bilan foydalanishni ko'rib chiqdik. Bu bo'limda biz Geb dan foydalanamiz, testlarimizni yanada Groovy-yo'naltirilgan qiladi.

Nima uchun Geb va MockMvc?

Geb WebDriver tomonidan qo'llab-quvvatlanadi, shuning uchun u bizga xuddi shu afzalliklarni beradi. Biroq, Geb yanada soddaroq bo'lishni oshiradi, chunki u stereotip kodni biz uchun amalga oshiradi.

MockMvc va Gebni sozlash

Quyidagi usulda MockMvcdan foydalanadigan Selenium WebDriver yordamida Gebdan Browser ni osongina ishga tushirish mumkin:

def setup() {
    browser.driver = MockMvcHtmlUnitDriverBuilder
        .webAppContextSetup(context)
        .build()
}
Bu MockMvcHtmlUnitDriverBuilder dan foydalanishning oddiy misoli. Kengaytirilgan foydalanish parametrlarini "MockMvcHtmlUnitDriverBuilder kengaytirilgan foydalanish" bo'limida ko'rishingiz mumkin.

Shunday qilib, localhostni server sifatida ishlatadigan har qanday URL-manzilni haqiqiy HTTP ulanishisiz bevosita bizning MockMvc instancesiga yo'naltirilishi mumkin. Har qanday boshqa URL-manzil so'rash odatdagidek tarmoq aloqasi orqali amalga oshiriladi. Bu bizga kontent yetkazib berish tarmoqlari (CDN)dan foydalanishni test qilishni osonlashtiradi.

MockMvc va Gebni ishlatish

Endi biz Gebni odatdagidek ishlatishimiz mumkin, lekin ilovamizni servlet konteyneriga joylashtirishga hojat qolmaydi. Masalan, xabar yaratish uchun quyidagi ko'rinishni so'rash

Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION