Последнее время мучает одна идея. Мне как всегда кажется, что она реально не нова, и если это так, скажите мне, как это дело называется.
Если помните, я уже писал о тестировании наследниками. Теперь у меня родилась идея организации инлайн тестов, также намного своеобразным способом.
Что если проект разделить на 2, или создавать 2 его копии. Один будет для боевого сервера, а второй будет для сервера разработки. И проект для разработки будет отличатся от проекта для боевого только тем, что в него будут включены проверки входных данных для всех блоков программы ( или критичных, или точек взаимодействия модулей ), это бывает полезно, когда несколько человек работает на одним проекта и надо проверять, чтоб один разработки пользовался интерфейсом модуля другого разработчика правильно. Но все эти проверки не нужны на боевом, или не нужны с определенного времени. Т.е. чтоб от них можно было легко избавится и легко включить обратно.
Так. Надеюсь общую задачу я описал адекватно. Если нет, то возможно из решения вы поймете о чем речь.
Задача решается на python.
У python есть массив каталогов, в которых он ищет модуль, как только вы хотите его импортировать. И этот список каталогов можно расширять во время работы программы.
-
import sys
-
sys.path.apend('/home/oduvan/list_of_my_cool_modules')
Python позволяет один и тот же модуль импортировать с разными именами. Т.е. в области видимости, в которую мы импортируем модуль — мы можем дать ему несколько различных имен.
-
import sys
-
import sys as new_sys
И можно все имена из одного модуля импортировать в другой модуль.
-
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
-
def p_name():
-
print 'Production'
-
-
def just_in_prod():
-
print 'This just in prod'
-
-
def sum2(a,b):
-
return a+b
-
-
class A(object):
-
def b_prop(self,b):
-
return b.prop
-
-
class B(object):
-
def __init__(self,prop):
-
self.prop = prop
функция p_name просто нужна для вывода имени модуля или репозитария, для нас она особой ценности не имеет.
just_in_prod — тестироваться этой функции проводится не будет
sum2 — складывает 2 числа. И мы хотим проверить, чтоб это на самом деле были 2 числа.
Объект класс A имеет метод, который на вход получает один параметр — объект класса B, и возвращает его свойство. Класс B мы также тестировать не будем.
Как сами уже можете видеть, в модуле нет ни одного слово о тестах. Вы видите чистый функционал.
Теперь приведу вам пример модуля t1 из develop
-
from production.t1 import *
-
import production.t1 as P
-
def p_name():
-
print 'Develop'
-
P.p_name()
-
-
def sum2(a,b):
-
if not isinstance(a, (int,float)):
-
raise ValueError('a must be int or float')
-
if not isinstance(b, (int,float)):
-
raise ValueError('a must be int or float')
-
return P.sum2(a,b)
-
-
-
class A(P.A):
-
def b_prop(self,b,*args,**kwargs):
-
if not isinstance(b, B):
-
raise ValueError('first argument must be instance of B')
-
return super(A,self).b_prop(b,*args,**kwargs)
Первые 2 строчки всегда должны быть в модуле тестирования.
Функция p_name будет чисто информативная ( мы на ней покажем вам порядок импортирования) , и безусловно тестирование без нее лучше обойтись.
Проверка для функции sum2 и проверка для свойства b_prop из класса A.
Ну и текст скрипта, который демонстрирует процесс работы и тестирования run.py. Предполагается, что у вас это и будет точкой отсчета для запуска.
-
import sys
-
sys.path.append('modules/develop')
-
sys.path.append('modules')
-
sys.path.append('modules/production')
-
-
import t1,t2
-
t1.p_name()
-
print '—'
-
t1.just_in_prod()
-
t2.in_only_production()
-
print '—'
-
o_a = t1.A()
-
o_b = t1.B('it_s prop')
-
print o_a.b_prop(o_b)
-
-
print t1.sum2(1,2)
-
-
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, inprint 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
для боевого сервера он будет такой же, только в «шапке» скрипта будет не
-
import sys
-
sys.path.append('modules/develop')
-
sys.path.append('modules')
-
sys.path.append('modules/production')
а
-
import sys
-
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, inprint 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
Вот и все. Жду жесткую критику, предложения по улучшению.
Спасибо, что дочитали до конца


Коротенько изложу идею. И как всегда, уверен, что она не нова, т.к. ничего особенного в ней нет.
Recent Comments