Отправка объекта C из класса A в класс B - PullRequest
3 голосов
/ 02 марта 2011

Я не могу понять, как проектировать классы в моей системе.

В классе A я создаю объект selenium (он имитирует действия пользователя на веб-сайте).

В этом ClassA я создаю другойтакие объекты, как SearchScreen, Payment_Screen и Summary_Screen.

# -*- coding: utf-8 -*-
from selenium import selenium
import unittest, time, re

class OurSiteTestCases(unittest.TestCase):
    def setUp(self):
        self.verificationErrors = []

        self.selenium = selenium("localhost", 5555, "*chrome", "http://www.someaddress.com/")
        time.sleep(5)
        self.selenium.start()        

    def test_buy_coffee(self):

        sel = self.selenium

        sel.open('/')
        sel.window_maximize()

        search_screen=SearchScreen(self.selenium)
        search_screen.choose('lavazza')

        payment_screen=PaymentScreen(self.selenium)
        payment_screen.fill_test_data()

        summary_screen=SummaryScreen(selenium)
        summary_screen.accept()


    def tearDown(self):
        self.selenium.stop()
        self.assertEqual([], self.verificationErrors)

if __name__ == "__main__":
    unittest.main()

Это пример модуля SearchScreen:

class SearchScreen:
    def __init__(self,selenium):
        self.selenium=selenium

    def search(self):
        self.selenium.click('css=button.search')

Я хочу знать, есть ли что-нибудь в порядке с дизайном этих классов?

Ответы [ 3 ]

3 голосов
/ 05 марта 2011

Ваш подход в порядке. У вас есть набор классов инструментов, каждый из которых должен знать свою цель. Затем у вас есть класс инструментария, который координирует эти инструменты для конкретной цели.

class AgreePrice:
    def __init__(self, connection): ...

class PlaceOrder:
    def __init__(self, connection): ...

class ConfirmAvailability:
    def __init__(self, connection): ...

class BookingService:
    def __init__(self, connection): ...

    def book(self): 
        for Command in (ConfirmAvailability, AgreePrice, PlaceOrder):
            command = Command(self.connection)
            command.run()
            assert command.success()

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

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

Как правило, вы хотите убедиться, что ваши классы "инструмента" (SearchScreen и т. Д.) Находятся на концептуально более низком уровне, чем ваш контроллер (ваши тестовые примеры). Какие они для тебя.

Простейшие классы этих инструментов являются формой шаблона проектирования Object Object . Хотя в вашем случае вы вызываете более одного метода для каждого объекта, они немного сложнее.


Или, короче говоря. Ваш дизайн в порядке и очень распространен.

3 голосов
/ 02 марта 2011

Если SearchScreen / PaymentScreen / SummaryScreen выполняет только тестовую логику, мне кажется, что вы могли бы просто использовать эту логику в служебных методах OurSiteTestCases.

Возможный дизайн для метода test_buy_coffee (в зависимости от того, что вы на самом деле делаете в SearchScreen и др.):

def test_buy_coffee(self):

    sel = self.selenium

    sel.open('/')
    sel.window_maximize()

    # Replace SearchScreen
    self.__choose_search()
    # Replace PaymentScreen
    self.__fill_payment_data()
    # Replace SummaryScreen
    self.__accept_summary()

Edit: Если вам нужно выделить логику тестирования в __choose_search, __fill_payment_data и __accept_summary, чтобы разделить ее между тестами, вы можете написать соответствующие функции тестирования утилиты в общем модуле / пакете. Или вы можете написать тестовый базовый класс, который содержит объект селена (self.selenium) и имеет «защищенные» служебные методы _choose_search, _fill_payment_data и _accept_summary. Все зависит от того, что практично в вашем случае:)

1 голос
/ 06 марта 2011

Может быть, вы можете использовать шаблон цепочки ответственности:

Определение: Старайтесь не связывать отправителя запроса с получателем, предоставляя более чем одному объекту возможность обработать запрос. Цепочка получения объектов и передачи запроса по цепочке, пока объект не обрабатывает его.

Пример в c # the:

  Handler h1 = new ConcreteHandler1();

  Handler h2 = new ConcreteHandler2();

  Handler h3 = new ConcreteHandler3();

  h1.SetSuccessor(h2);

  h2.SetSuccessor(h3);



  // Generate and process request

  int[] requests = { 2, 5, 14, 22, 18, 3, 27, 20 };


  foreach (int request in requests)
  {
    h1.HandleRequest(request);
  }

Здесь вы можете увидеть полную документацию: http://www.dofactory.com/Patterns/PatternChain.aspx#_self1

...