2015-08-25 4 views
4

Я использую SitePrism для тестирования своего веб-приложения. У меня есть несколько классов, которые проходят SitePrism::Page и ряд часто используемых HTML-фрагменты представлены путем сопоставления классов простирающегося SitePrism::SectionКак добавить раздел к объекту страницы SitePrism динамически?

class Login < SitePrism::Section 
    element :username, "#username" 
    element :password, "#password" 
    element :sign_in, "button" 
end 

class Home < SitePrism::Page 
    section :login, Login, "div.login" 
end 

Проблема, приложение, я работаю над основана на CMS, в которую можно собрать, выбрав шаблон Template на основе предварительно определенного содержимого, а затем перетащив на страницу любое количество доступных компонентов.

Начальные разработчики создали объект Страница объекта для зеркального отображения всех доступных Шаблон. Это было прекрасно, пока количество тестов было низким, и не было слишком много вариантов страниц, которые мы должны были тестировать в наших файлах функций.

С добавлением нескольких тестовых примеров объекты страницы начали расти с угрожающей скоростью.

В то время как мы можем легко уменьшить дублирование кода путем определения разделов для каждого компонента, доступного в CMS и повторное использование их через страницу Объекты, там просто много свойств, которые редко привыкают.

class BlogPost < SitePrism::Page 

    section :logo, MySite::Components::Logo, '.logo'  
    section :navigation, MySite::Components::Navigation, '.primary-navigation' 
    section :header, MySite::Components::BlogHeader, '.header' 
    section :introduction, MySite::Components::Text, '.text .intro' 
    # and so on, a lot of dynamic staff that could potentially be dropped onto the page 
    # but does not neccessarily be there, going in dozens of lines 
end 

Есть ли способ в SitePrism динамически добавить раздел к экземпляру страницы объекта в отличие от целого класса?

Then(/^Some step$/) do 
    @blog = PageObjects::BlogPost.new() 
    @blog.load("some url") 
    @blog.somehow_add_a_section_here_dynamically 
    expect (@blog.some_added_section).to be_visible 
end 

Также меня беспокоит, что делать что-то подобное будет потенциально привести к CSS селекторы просочиться в ступенчатых определений, которые, как правило, плохая практика.

Другим способом обойти это было бы создание объектов страницы для конкретных примеров страниц в отличие от универсальных шаблонов. Объекты Template Page 0 могут содержать только то, что испечено в шаблонах, и расширяться другими объектами . Объекты, которые отражают определенные страницы, заботясь о различиях. Это похоже на гораздо более чистый подход, поэтому я, вероятно, собираюсь написать свои тесты таким образом

В любом случае, техническая часть вопроса стоит. Независимо от того, насколько это хорошо или плохо, как я могу динамически расширить объект страницы с помощью дополнительного раздела? Мне просто интересно.

ответ

4

я был в какой-то момент хотел бы сделать то, что вы говорите о для почти той же самой причине. У нас были страницы, в которые могли вставляться новые разделы контента; делая их очень динамичными. Я экспериментировал с путями сделать это и никогда не нашел того, что мне особенно понравилось.

Методы, такие как element и sections в site-prism, каждый определяет ряд методов для класса. Вы можете позвонить MyPage.section в свой тест или добавить метод, который вызывает self.class.section, и использовать его для добавления в новые разделы. Но они будут существовать для всех экземпляров этой страницы; вероятно, не то, что вы хотите.

Вы можете альтернативно прикрепить их к через singleton_class:

my_page = MyPage.new 
my_page.singleton_class.section(:new_section, NewSection, '#foo') 

Но это становится немного некрасиво, чтобы бросить в ваших тестах, не так ли?

Я долго думал, что Секции должны иметь default_locator (но трудно получить патчи принимаются)
С, что мы могли бы обобщить это немного:

class DynamicSection < SitePrism::Section 
    def self.set_default_locator(locator) 
    @default_locator = locator 
    end 
    def self.default_locator 
    @default_locator 
    end 
end  

class DynamicPage < SitePrism::Page 
    # add sections (and related methods) to this instance of the page 
    def include_sections(*syms) 
    syms.each do |sym| 
     klass = sym.to_s.camelize.constantize 
     self.singleton_class.section(sym, klass, klass.default_locator) 
    end 
    end 
end 

И тогда вы можете использовать их в качестве родителей ,

class FooSection < DynamicSection 
    set_default_locator '#foo' 
    element :username, "#username" 
end 

class BlogPostPage < DynamicPage 
    # elements that exist on every BlogPost 
end 

В тестах:

@page = BlogPostPage.new 
@page.include_sections(:foo_section, :bar_section) 
expect(@page.foo_section).to be_visible 

С другой стороны-это действительно может быть проще просто создать несколько различных вариантов страницы-объекта для использования в тестах. (Вы действительно собираетесь проверить, что много вариантов? Может быть, может быть, нет.)

+0

Как уже упоминалось в самом вопросе. Я более склонен следить за шаблоном _page на странице_странице, а не _page-объектом на шаблон_. Я просто оставлю шаблоны базовыми классами или миксинами. Мне было просто любопытно, как это можно сделать с точки зрения метапрограмм в Ruby. – toniedzwiedz

+1

Оказалось, интересно и интересно пересмотреть эту проблему; поэтому я рад, что вы его спросили :-) Я мог бы включить что-то подобное на моем развилке вперед. – tgf

1

Вы можете добавить раздел только к экземпляру объекта страницы, изменив его одноэлементный класс.

Then(/^Some step$/) do 
    @blog = PageObjects::BlogPost.new 
    @blog.load("some url") 

    # You can see that @blog does not have the logo section 
    expect(@blog).not_to respond_to(:logo) 

    # Add a section to just the one instance of BlogPost 
    class << @blog 
    section(:logo, MySite::Components::Logo, '.logo') 
    end 

    # You can now see that #blog has the logo section 
    expect(@blog).to respond_to(:logo) 
end 

Это, скорее всего, приведет к дублированию определения раздела в несколько этапов. Чтобы решить эту проблему, вы можете создать метод в пределах BlogPost для динамического добавления указанных разделов.

В следующем классе BlogPost создан словарь доступных компонентов. Класс имеет метод, который добавляет компоненты на основе определения словаря.

class BlogPost < SitePrism::Page 
    COMPONENT_DICTIONARY = { 
    logo: {class: MySite::Components::Logo, selector: '.logo'}, 
    navigation: {class: MySite::Components::Navigation, selector: '.primary-navigation'}, 
    header: {class: MySite::Components::BlogHeader, selector: '.header'} 
    } 

    def add_components(*components) 
    Array(components).each do |component| 
     metaclass = class << self; self; end 
     metaclass.section(component, COMPONENT_DICTIONARY[component][:class], COMPONENT_DICTIONARY[component][:selector]) 
    end 
    end 
end 

В качестве примера использования:

# Create a blog post that just has the logo section 
@blog = BlogPost.new 
@blog.add_components(:logo) 

# Create a blog post that has the navigation and header section 
@blog2 = BlogPost.new 
@blog2.add_components(:navigation, :header) 

# Notice that each blog only has the added components 
expect(@blog).to respond_to(:logo) 
expect(@blog).not_to respond_to(:navigation) 
expect(@blog).not_to respond_to(:header) 

expect(@blog2).not_to respond_to(:logo) 
expect(@blog2).to respond_to(:navigation) 
expect(@blog2).to respond_to(:header) 
+0

Утешительный, собирающийся попробовать это завтра. – toniedzwiedz

+0

Джастин, вы выложили, пока я еще печатал, но похоже, что у нас очень похожие идеи здесь :-) – tgf