2017-02-22 13 views
1

У меня есть класс «Wrapper». Wrapper наследуется от объекта, и основная ответственность заключается в том, чтобы перехватывать вызовы метода селену во время теста. Это __init__ метод подписиПредоставить доступ к области возможностей для себя, где он называется?

__init__(self, object_to_wrap, actions={}, **kwargs) 

Часть его функциональности взять словарь в качестве аргумента в методе инициализации, и разоблачить ключи в качестве атрибутов на object_to_wrap объекта, определяя __getattr__ магический метод (подпись: __getattr__(self, item):) и проверка ключей self.actions на item. Если он найден, метод вызывается.

В тестовом коде инициализации будет выглядеть так:

def navigate(scoped_self, to=''): 
    self.driver.switch_to_default_content() 
    self.driver.switch_to.frame(to) 
    scoped_self.navigations.append(to) 
    # the navigate method is scoped in an 
    # instance method of the test class, so it has access to self 

Итак, мой вопрос, как я могу сделать сферу, или self в описанном выше способе, быть сфера моего Wrapper класса , а не тест-класс?

Для ясности, если решение, которое я ищу в этом вопросе было найдено, реализация навигации изменится быть:

def navigate(scoped_self, to=''): 
    self.object_to_wrap.switch_to_default_content() 
    self.object_to_wrap.switch_to.frame(to) 

Кроме того, для ясности, я уверен, что я ищу это именно то, что выполняет Javascripts Function.prototype.bind.

UPDATE: Определение методов, такие как плавание, внутри класса Wrapper это не вариант, как класс Упаковочного не есть может тест реализующей логики. Набор тестов выполнен на n> 1 DOM, которые полностью не связаны. Например, один из тестов требует метода «alert_handlers» (для перезаписывания функции window.alert и возврата предупреждений, представленных в виде строки), другой может потребовать метод навигации, а третий может потребовать и то, и другое.

UPDATE # 2: Благодаря ниже ответ от c17r, я понял, что без GetAttribute код, включенный в моем примере, что, казалось бы, как если бы я просил что-то мы уже выполнена. То, что я ищу, - это способность, в приведенном выше методе навигации, для scoped_self быть экземпляром Wrapper.

Кроме того, я специально искал способ «динамически» передать scoped_self к функциям (таким образом, что Function.prototype.bind «динамически» устанавливает this где this является myEventListener.bind (окно), если вы не знакомы с JavaScript, сделать слушатель событий на теле и console.log thisбез связывания, а затем с креплениями, чтобы увидеть разницу)

Кроме того, я полагал, что это может помочь привести пример реализации без решения Я ищу для. Это в настоящее время работает, как ожидалось:

class Wrapper(object): 
    def __init__(self, wrapped, actions={}): 
     self.wrapped = wrapped 
     self.actions = actions 
     self.navigations = [] # EXAMPLE, SEE THE TEST CLASS CODE 

    def __getattr__(self, item): 
     if item in self.actions: 
      return self.actions[item] 
     # do other fancy stuff here 
     # UPDATE #2: added for clarity. this is the current implementation 
     orig_attr = self.wrapped.__getattribute__(item) 
     if callable(orig_attr): 
      def hooked(*args, **kwargs): 
       self.pre(item, *args, **kwargs) 
       self.err = False 
       try: 
        result = orig_attr(*args, **kwargs) 
       except Exception as e: 
        #logs 
        self.post(*args, **kwargs) 
        raise 
      if type(self.wrapped) == type(result): 
       return self 
      return result 
     return hooked 
    else: 
     return orig_attr 

class SomeTest(): 
    #blah blah init stuff, set self.driver = selenium.webdriver.Phantomjs 
    def spawn_actions(self): 
     def navigate(scoped_self, to=''): 
      self.driver.switch_to_default_content() 
      self.driver.switch_to.frame(to) 
      scoped_self.navigations.append(to) # <--- appended to wrapper.navigations  
     return {'navigate': navigate} 

    def run(self): 
     driver = Wrapper(self.driver, self.spawn_actions()) 
     driver.get(url) 
     driver.navigate('New Request') 
     # fun tests! 

ответ

0

Если я вас правильно понимаю, 3 вещи:

  1. Wrapper нужно будет пройти все неизвестные функции вплоть до обернутого элемента, в противном случае driver.get() не будет работать должным образом ,
  2. navigate функция должна self в качестве первого параметра, как если бы она была на самом деле определена на Wrapper
  3. Wrapper потребности передать self в функцию Dict основе. Это немного сложно, так как __getattr__ фактически не вызывает функцию, поэтому вам нужно вернуть функцию, которая правильно вызывает базовую функцию, подобно тому, как работают декораторы.

Как это:

class Driver(object): 
    def get(self, url): 
     print('get') 
     print(repr(self)) 
     print(repr(url)) 
     print('--') 


class Wrapper(object): 
    def __init__(self, wrapped, actions={}): 
     self.wrapped = wrapped 
     self.actions = actions 

    def __getattr__(self, item):    
     if item in self.actions: 
      def unwrap(*args, **kwargs): 
       return self.actions[item](self, *args, **kwargs) 
      return unwrap 
     else: 
      return getattr(self.wrapped, item) 


class Test(object): 
    def __init__(self): 
     self.driver = Driver(); 

    def spawn_actions(self): 
     def navigate(self, to=''): 
      print('navigate') 
      print(repr(self)) 
      print(repr(to)) 
      print(repr(self.wrapped)) 
      print('--') 

     return { 
      'navigate': navigate 
     } 

    def run(self): 
     driver = Wrapper(self.driver, self.spawn_actions()) 
     driver.get('url') 
     driver.navigate('thing') 

Теперь телефону:

t = Test() 
t.run() 

Выходы:

get 
<__main__.Driver object at 0x104008630> 
'url' 
-- 
navigate 
<__main__.Wrapper object at 0x104008ba8> 
'thing' 
<__main__.Driver object at 0x104008630> 
-- 

EDIT

Вы можете также динамически связать методы к примеру, вместо __getattr__ возвращения функции unwrap:

import types 
class Wrapper(object): 
    def __init__(self, wrapped, actions={}): 
     self.wrapped = wrapped 

     for name, func in actions.items(): 
      setattr(self, name, types.MethodType(func, self)) 

    def __getattr__(self, item): 
     return getattr(self.wrapped, item) 
+0

Ты правильно меня поняли, и я извиняюсь за оставляя все __getattr__ (для краткости). см. мое обновление о том, как выглядит фактическая реализация. Обратите внимание, что будет вызвано более одного метода экземпляра, который я не буду включать в код снова для краткости (и потому, что он слишком близок к производственному коду для нашего продукта) – h3xc0ntr0l

+0

Однако, тщательно перечитав свой код, я считаю, я ответил на мой вопрос :) Я проверю, как только я доберусь до машины, где у меня есть реализация. – h3xc0ntr0l

Смежные вопросы