Categories

Checkio.ORG

Subscribe to Posts

Email:

  • 04Aug

    kombainПоследнее время мучает одна идея. Мне как всегда кажется, что она реально не нова, и если это так, скажите мне, как это дело называется.

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

    Что если проект разделить на 2, или создавать 2 его копии. Один будет для боевого сервера, а второй будет для сервера разработки. И проект для разработки будет отличатся от проекта для боевого только тем, что в него будут включены проверки входных данных для всех блоков программы ( или критичных, или точек взаимодействия модулей ), это бывает полезно, когда несколько человек работает на одним проекта и надо проверять, чтоб один разработки пользовался интерфейсом модуля другого разработчика правильно. Но все эти проверки не нужны на боевом, или не нужны с определенного времени. Т.е. чтоб от них можно было легко избавится и легко включить обратно.

    Так. Надеюсь общую задачу я описал адекватно. Если нет, то возможно из решения вы поймете о чем речь.

    Задача решается на python.

    У python есть массив каталогов, в которых он ищет модуль, как только вы хотите его импортировать. И этот список каталогов можно расширять во время работы программы.

    1. import sys
    2. sys.path.apend('/home/oduvan/list_of_my_cool_modules')

    Python позволяет один и тот же модуль импортировать с разными именами. Т.е. в области видимости, в которую мы импортируем модуль — мы можем дать ему несколько различных имен.

    1. import sys
    2. import sys as new_sys

    И можно все имена из одного модуля импортировать в другой модуль.

    1. from sys import *

    Вот по сути и весь необходимый функционал от python, который необходим для реализации этой идеи. Т.е. предполагаю, что любой другой язык с ООП, который имеет эти возможности может использовать эту идею.

    Решение.

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

    Ниже приведена структура каталогов и файлов нашего проекта.

    modules
    	__init__.py
    	develop
    		__init__.py
    		t1.py
    	production
    		__init__.py
    		t1.py
    		t2.py
    run.py
    

    В modules/production собраны все модули для боевого сервера
    В modules/develop модули с проверками данных
    run.py скрипт, в котором они будут использоваться.

    Замете, что в папке modules тоже есть файл __init__.py, т.е. он также будет точкой отсчета для импорта.

    В папке develop есть файл t1.py для тестирования такого же модуля из продакшена, или его части. А также мы видим, что модуль t2 остается без тестов.

    ( Автор просит прощения, за такие без звучные имена, с фантазией у него совсем все плохо )

    Приведу пример модуля t1 из production

    1. def p_name():
    2.     print 'Production'
    3.    
    4. def just_in_prod():
    5.     print 'This just in prod'
    6.  
    7. def sum2(a,b):
    8.     return a+b
    9.    
    10. class A(object):
    11.     def b_prop(self,b):
    12.         return b.prop
    13.  
    14. class B(object):
    15.     def __init__(self,prop):
    16.         self.prop = prop

    функция p_name просто нужна для вывода имени модуля или репозитария, для нас она особой ценности не имеет.
    just_in_prod — тестироваться этой функции проводится не будет
    sum2 — складывает 2 числа. И мы хотим проверить, чтоб это на самом деле были 2 числа.
    Объект класс A имеет метод, который на вход получает один параметр — объект класса B, и возвращает его свойство. Класс B мы также тестировать не будем.

    Как сами уже можете видеть, в модуле нет ни одного слово о тестах. Вы видите чистый функционал.

    Теперь приведу вам пример модуля t1 из develop

    1. from production.t1 import *
    2. import production.t1 as P
    3. def p_name():
    4.     print 'Develop'
    5.     P.p_name()
    6.  
    7. def sum2(a,b):
    8.     if not isinstance(a, (int,float)):
    9.         raise ValueError('a must be int or float')
    10.     if not isinstance(b, (int,float)):
    11.         raise ValueError('a must be int or float')
    12.     return P.sum2(a,b)
    13.    
    14.    
    15. class A(P.A):
    16.     def b_prop(self,b,*args,**kwargs):
    17.         if not isinstance(b, B):
    18.             raise ValueError('first argument must be instance of B')
    19.         return super(A,self).b_prop(b,*args,**kwargs)

    Первые 2 строчки всегда должны быть в модуле тестирования.

    Функция p_name будет чисто информативная ( мы на ней покажем вам порядок импортирования) , и безусловно тестирование без нее лучше обойтись.

    Проверка для функции sum2 и проверка для свойства b_prop из класса A.

    Ну и текст скрипта, который демонстрирует процесс работы и тестирования run.py. Предполагается, что у вас это и будет точкой отсчета для запуска.

    1. import sys
    2. sys.path.append('modules/develop')
    3. sys.path.append('modules')
    4. sys.path.append('modules/production')
    5.  
    6. import t1,t2
    7. t1.p_name()
    8. print '—'
    9. t1.just_in_prod()
    10. t2.in_only_production()
    11. print '—'
    12. o_a = t1.A()
    13. o_b = t1.B('it_s prop')
    14. print o_a.b_prop(o_b)
    15.  
    16. print t1.sum2(1,2)
    17.  
    18. print o_a.b_prop(15)

    В этом скрипте важным является порядок добавления модулей в массив sys.path, ну и то, чтоб эти строчки были самые первые в вашем скрипте.

    Вывод скрипта будет следующий

    Develop
    Production
    ---
    This just in prod
    Only in production
    ---
    it_s prop
    3
    Traceback (most recent call last):
      File "run.py", line 18, in 
        print o_a.b_prop(15)
      File "modules/develop/t1.py", line 18, in b_prop
        raise ValueError('first argument must be instance of B')
    ValueError: first argument must be instance of B
    

    для боевого сервера он будет такой же, только в «шапке» скрипта будет не

    1. import sys
    2. sys.path.append('modules/develop')
    3. sys.path.append('modules')
    4. sys.path.append('modules/production')

    а

    1. import sys
    2. sys.path.append('modules/production')

    В этом случае поменяется только текст ошибки

    Production
    ---
    This just in prod
    Only in production
    ---
    it_s prop
    3
    Traceback (most recent call last):
      File "run.py", line 18, in 
        print o_a.b_prop(15)
      File "modules/production/t1.py", line 12, in b_prop
        return b.prop
    AttributeError: 'int' object has no attribute 'prop'
    

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

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

    Я же планирую у себя это использовать для своего сокет сервера. У меня именно так разнесен скрипт run.pay от функциональных модулей. И думаю при запуске его добавить еще параметр -t которому можно будет передать имя профиля тестирования.

    Файлы из примера split_develop.tar

    Вот и все. Жду жесткую критику, предложения по улучшению.

    Спасибо, что дочитали до конца :)

    Share and Enjoy:
    • Facebook
    • LinkedIn
    • Twitter
    • del.icio.us
    • StumbleUpon
    • MySpace
    • Reddit
    • Digg
    • Google Bookmarks
    • Technorati
    • email
    • Print
    • Sphinn
    • Mixx
    • Blogplay
    • Add to favorites
    • Linkter
    • Live
    • MSN Reporter
    • NewsVine
    • RSS
    • Yahoo! Bookmarks
    • Yahoo! Buzz
    • Yigg
    Rating 3.00 out of 5
    [?]

    Posted by Oduvan @ 11:38 am

    Tags: , , ,

blog comments powered by Disqus