Python for testers

[Рецепт] Создание вложенных словарей для тестовых нужд без особых проблем, пример на Python

Written by Михаил Поляруш on . Posted in Автоматизация, Работаю, Разработка

В поддержку новой инициативы Code Recipes. Искренне надеюсь на вашу помощь и всяческую поддержку в виде новых code recipes!

Если Вы программируете на Python, то знаете что словарь является очень важной структурой. И не только является важным для самого языка программирования, а и для автоматизации тестирования на этом языке. Ведь очень часто приходиться описывать данные в формате ключ: значение. Такие данные могут быть большие по объему описываемых данных, а также не существовать в момент обращения к словарю . И если использовать стандартные механизмы словаря, то описать сложенные подсловари неудобно, а тем более если вы хотите создавать структуру данных программно в режиме runtime.

Пример, нам надо создать структуру:

{
    "server": {
        "host": "127.0.0.1",
        "port": "22"
    },
    "configuration": {
        "ssh": {
            "access": "true",
            "login": "some",
            "password": "some"
        }
    }
}

Стандартный способ решения этой задачи

data = {}
# some logic here
print data["server"]  # will raise exception due to 'KeyError: 'server''
# some logic here
data["server"] = {
    "host": "127.0.0.1",
    "port": "22"
}
# some logic here
if data["configuration"]["ssh"]["login"]: # will raise exception here
    pass

# some logic here
data["configuration"] = {
    "ssh": {
    "access": "true",
    "login": "some",
    "password": "some"
    }
}

Немного улучшенный вариант

У словаря можно использовать get метод, чтобы получить значение и не выдавать исключение и возвращать, дефолтное значение. Но если в случае с data["server"] это сработает, то сdata["configuration"]["ssh"]["login"] не сработает, так как возвращаемый объект будет типа None

data = {}
# some logic here
print data.get("server")
# some logic here
data["server"] = {
    "host": "127.0.0.1",
    "port": "22"
}
# some logic here
if data.get("configuration").get("ssh").get("login"):
    # will raise exception
    # AttributeError: 'NoneType' object has no attribute 'get'
    pass

# some logic here
data["configuration"] = {
    "ssh": {
    "access": "true",
    "login": "some",
    "password": "some"
    }
}

Но если задать дефолтные значения дляdata.get("configuration").get("ssh").get("login") чтобы не получать ошибкуAttributeError: 'NoneType' object has no attribute 'get', то можно получить нормальный результат

data = {}
# some logic here
print data.get("server")
# some logic here
data["server"] = {
    "host": "127.0.0.1",
    "port": "22"
}
# some logic here
if data.get("configuration", {}).get("ssh", {}).get("login", {}):
    # will raise exception
    # AttributeError: 'NoneType' object has no attribute 'get'
    pass

# some logic here
data["configuration"] = {
    "ssh": {
    "access": "true",
    "login": "some",
    "password": "some"
    }
}

Готовый рецепт, усовершенствованный способ

А можно использовать возможности defaultdict

from collections import defaultdict
_default_data = lambda: defaultdict(_default_data)
data = _default_data()
# some logic here
print data["server"]
# some logic here
data["server"]["host"] = "127.0.0.1"
data["server"]["port"] = "22"
# some logic here
if data["configuration"]["ssh"]["login"]:
    print ("some logic") 
# some logic here
data["configuration"]["ssh"]["access"] = "true"
data["configuration"]["ssh"]["login"] = "some"
data["configuration"]["ssh"]["password"] = "some"

import json
print json.dumps(data, indent=2)
defaultdict(<function <lambda> at 0x02565930>, {})
{
  "configuration": {
    "ssh": {
      "access": "true", 
      "login": "some", 
      "password": "some"
    }
  }, 
  "server": {
    "host": "127.0.0.1", 
    "port": "22"
  }
}

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

Все варианты кода можно посмотреть на нашей общем репозитории примеровhttps://github.com/atinfo/at.info-knowledge-base/tree/master/programming/python/code%20recipes/generate%20nested%20dicts

Ну и кто хочет учиться python и автоматизации, милости прошу на http://lessons2.ru

Разделение stdout и stderr в python nosetest

Written by Михаил Поляруш on . Posted in Автоматизация, Работаю, Разработка

Проблема: в python есть популярный xUnit фреймворк nose, которым пользуются довольно много людей.  Когда подключаешь nose к continuous integration, то хочешь чтобы результаты были максимально видимыми и репрезентативными.  Но вот есть одна проблема, всю выходную информацию nose записывает в один поток, либо stdout либо stderr.  А как же быть, если нам нужно показать часть в stderr, а часть в stdout?

Например 

Результат прогона полностью выводиться в stderr:

#1 Test 1 … ok
#2 Test 2 … FAIL
#3 Test 3 … ok
#4 Test 4 … ok
#5 Test 5 … FAIL

======================================================================
FAIL: Test 2
———————————————————————-
Traceback (most recent call last):
  File “/home/katerina/Desktop/my_tests/color.py”, line 11, in test_2
    self.assertEquals(1, 2)
AssertionError: 1 != 2

======================================================================
FAIL: Test 5
———————————————————————-
Traceback (most recent call last):
  File “/home/katerina/Desktop/my_tests/color.py”, line 23, in test_5
    self.assertEquals(1, 7)
AssertionError: 1 != 7

———————————————————————-
XML: nosetests.xml
———————————————————————-
Ran 5 tests in 0.004s

FAILED (failures=2)


А надо чтоб результат выводился в 2 потока:

#1 Test 1 … ok
#2 Test 2 … FAIL
#3 Test 3 … ok
#4 Test 4 … ok
#5 Test 5 … FAIL

======================================================================
FAIL: Test 2
———————————————————————-
Traceback (most recent call last):
  File “/home/katerina/Desktop/my_tests/color.py”, line 11, in test_2
    self.assertEquals(1, 2)
AssertionError: 1 != 2

======================================================================
FAIL: Test 5
———————————————————————-
Traceback (most recent call last):
  File “/home/katerina/Desktop/my_tests/color.py”, line 23, in test_5
    self.assertEquals(1, 7)
AssertionError: 1 != 7

———————————————————————-
XML: nosetests.xml
———————————————————————-
Ran 5 tests in 0.004s

FAILED (failures=2)

Вся соль в том, что nose основывает весь свой запуск на unittest. А в unittest используется механизм буферизации stdout и stderr, т.е. unittest все ловит, но в последствии все уходит в один поток. Этот поток по умолчанию stderr. Его конечно можно поменять, но все равно это запись содержимого прогона в один поток или stderr или stdout.

Решение: простое решение – это переопределить TextTestResult и TextTestRunner при запуске тестов. Смотрим пример:

import unittest
import sys
import nose
from nose.core import TextTestRunner
from nose.result import TextTestResult
from cStringIO import StringIO

class Tests(unittest.TestCase):

    def test_1(self):
        """Test 1"""
        print 'something'
        self.assertEquals(1, 1)

    def test_2(self):
        """Test 2"""
        self.assertEquals(1, 1)

    def test_3(self):
        """Test 3"""
        self.assertEquals(1, 1)

    def test_4(self):
        """Test 4"""
        self.assertEquals(1, 1)

    def test_5(self):
        """Test 5"""
        print 'something'
        raise AssertionError("error")
        self.assertEquals(1, 1)

if __name__ == '__main__':
    class MyTextTestResult(TextTestResult):

        def addError(self, test, err):
            print >> sys.stderr, "%r ... error\n\t%r" % (test, err)

        def addFailure(self, test, err):
            print >> sys.stderr, "%r ... failure\n\t%r" % (test, err)

        def addSuccess(self, test):
            print >> sys.stdout, "%r ... ok" % test

    class MyTextTestRunner(TextTestRunner):

        def __init__(self):
            TextTestRunner.__init__(self, stream=StringIO())

        def _makeResult(self):
            return MyTextTestResult(self.stream,
                                    self.descriptions,
                                    self.verbosity,
                                    self.config)

    nose.main(testRunner=MyTextTestRunner())

Пример можно скачать здесь https://github.com/polusok/nose-split-stderr-stdout. Это далеко не идеальное решение, но позволит Вам быстро решить вашу задачу.

Успехов и хорошего питонирования!

З.Ы. С 05.08 стартует следующая группа по обучению программированию на python для начинающих. Еще можно записаться!

Логирование и декораторы в python

Written by Михаил Поляруш on . Posted in Автоматизация, Разработка

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

Как правильно выполнить логирование для Вашего проекта? Не знаю, каждый проект это отдельная история, которая должна рассматриваться в отдельности. (Обращайтесь, будем разбираться!) Но я хочу внести некоторые свои комментарии в данный процесс.

Меня попросили посмотреть на код и показать, как можно выводить документацию для каждого метода в лог. В python это лучше всего сделать с помощью декораторов. Декораторы – это мощная штука! В общем, меньше слов и больше кода.

import logging

def method_decorator(func):
    def wrapper(self, *argv, **kwargv):
        logging.basicConfig(filename='myapp.log', level=logging.INFO)
        logging.info(func.__doc__)
        return func(self, *argv, **kwargv)
    return wrapper

class Something1(object):

    @method_decorator
    def method1(self):
        """documentation thru method decorator for method 1"""
        pass

    def method2(self):
        """documentation thru method decorator for method 2"""
        pass

    @method_decorator
    def method3(self):
        """documentation thru method decorator for method 3"""
        pass

s1 = Something1()
s1.method1()
s1.method2()
s1.method3()

# or thru class decorator

def class_decorator(cls):
    for name, method in cls.__dict__.iteritems():
        if not name.startswith('_'):
            setattr(cls, name, method_decorator(method))
    return cls

@class_decorator
class Something2(object):

    def method1(self):
        """documentation thru class decorator for method 1"""
        pass

    def method2(self):
        """documentation thru class decorator for method 2"""
        pass

    def method3(self):
        """documentation thru class decorator for method 3"""
        pass

s2 = Something2()
s2.method1()
s2.method2()
s2.method3()

#check mixed execution
s1.method1()
s2.method1()

Т.е. мы описываем необходимый читабельный комментарий как docstring для методов, и когда эти методы вызываются, в лог записывается читабельная информация. В первом случае, можно использовать декоратор для методов, который можно использовать по требованию. А второй класс показывает, как можно включить данное логирование для всех методов класса. Или же Вы можете задать любую необходимую вам логику в class_decorator().

Вот, что мы получаем в логе:

INFO:root:documentation thru method decorator for method 1
INFO:root:documentation thru method decorator for method 3
INFO:root:documentation thru class decorator for method 1
INFO:root:documentation thru class decorator for method 2
INFO:root:documentation thru class decorator for method 3
INFO:root:documentation thru method decorator for method 1
INFO:root:documentation thru class decorator for method 1

Ну а теперь давайте подключим это к каким-то юнит-тестам. Например, это может выглядеть так.

import logging
import unittest
import random
from functools import wraps

def method_decorator(func):
    @wraps(func)
    def wrapper(self, *argv, **kwargv):
        logging.basicConfig(filename='myapp.log', 
            level=logging.INFO, format='%(message)s')
        logging.info("\t- %s" % func.__doc__)
        return func(self, *argv, **kwargv)
    return wrapper

def class_decorator(cls):
    for name, method in cls.__dict__.iteritems():
        if not name.startswith('_'):
            setattr(cls, name, method_decorator(method))
    return cls

class MyTestingException(Exception):

    def __init__(self, value):
        self.msg = value

    def __str__(self):
        return "%s\n%s" % (self.msg, open("myapp.log").read())

@class_decorator
class Something(object):

    def _generate_number(self):
        if random.randint(0, 10) == 5:
            raise MyTestingException("Please have a look to details:")
        return random.randint(0, 2)

    def method1(self):
        """log to system to make some actions"""
        return self._generate_number()

    def method2(self):
        """registered account with additional credits"""
        return self._generate_number()

    def method3(self):
        """buy subscription for defined account"""
        return self._generate_number()

class TestSomething(unittest.TestCase):

    def setUp(self):
        with open("myapp.log", "w") as f:
            f.truncate()
        self.s = Something()
        self.data = {'some data': [1, 2, 3]}

    def test_method1(self):
        self.s.method1()
        self.s.method2()
        self.s.method3()
        self.assertEquals(self.s.method1(), 0)

    def test_method2(self):
        self.s.method3()
        self.s.method2()
        self.s.method1()
        self.assertEquals(self.s.method2(), 1)

    def test_method3(self):
        self.s.method1()
        self.s.method3()
        self.s.method2()
        self.assertEquals(self.s.method3(), 2)

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

Что дает следующие результаты:

EFE

======================================================================

ERROR: test_method1 (__main__.TestSomething)

----------------------------------------------------------------------

Traceback (most recent call last):

  File "demo.py", line 64, in test_method1

    self.s.method3()

  File "demo.py", line 12, in wrapper

    return func(self, *argv, **kwargv)

  File "demo.py", line 50, in method3

    return self._generate_number()

  File "demo.py", line 37, in _generate_number

    raise MyTestingException("Please have a look to details:")

MyTestingException: Please have a look to details:

	- log to system to make some actions

	- registered account with additional credits

	- buy subscription for defined account

======================================================================

ERROR: test_method3 (__main__.TestSomething)

----------------------------------------------------------------------

Traceback (most recent call last):

  File "demo.py", line 77, in test_method3

    self.assertEquals(self.s.method3(), 2)

  File "demo.py", line 12, in wrapper

    return func(self, *argv, **kwargv)

  File "demo.py", line 50, in method3

    return self._generate_number()

  File "demo.py", line 37, in _generate_number

    raise MyTestingException("Please have a look to details:")

MyTestingException: Please have a look to details:

	- log to system to make some actions

	- buy subscription for defined account

	- registered account with additional credits

	- buy subscription for defined account

======================================================================

FAIL: test_method2 (__main__.TestSomething)

----------------------------------------------------------------------

Traceback (most recent call last):

  File "demo.py", line 71, in test_method2

    self.assertEquals(self.s.method2(), 1)

AssertionError: 2 != 1

----------------------------------------------------------------------

Ran 3 tests in 0.003s

FAILED (failures=1, errors=2)

Как всегда, пишите, если у Вас есть вопросы! Удачи и хорошего Вам питонирования! :)

Жизненные советы от Алекса Дубаса

Written by Михаил Поляруш on . Posted in Обыденное, Размышляю

Жизнь приносит нам слезы и радость, смех и разочарование. Люди, отношения, семья, здоровье, родители, зависть … Это лишь небольшая часть того, о чем мы думаем или, к сожалению, не думаем каждый день. А стоило бы. Ведь это и есть жизнь!

Да кстати о жизни, посмотрите видео. Не пожалеете!

А для тех, кто знает английский могут посмотреть английскую версию (оригинал) (как всегда, в СНГ сами сделать ничего не могут :) )

Как сдать ISTQB сертификацию в Украине?

Written by Михаил Поляруш on . Posted in Tестирование

Так как я являюсь обладателем аж 3х сертификаций данной организации, мне часто задают одни и те же вопросы. Вот недавно снова пришел подобный емейл. Значит надо оформить статью и потом высылать ее всем :) . Как я буду писать ее, да просто в формате вопросов, которые мне задают и моих ответов. Если Вы хотите спросить еще какие-то вопросы, you are welcome! Буду добавлять информацию по мере надобности.

  • Как проходит подготовка к Foundation Level экзамену?
    выдаются задания и есть определенное время
    вы решаете и cдаете и все :)
  • Вам было достаточно почитать примеры и доп. литературу?
    для меня да
    для других не сильно
    есть много ресурсов, где можно найти примерочные тесты
  • Основывается ли экзамен на общепризнанных прадигмах тестирования?
    :) интересный вопрос, но для меня не понятный, что Вы имеете ввиду
  • Какие основные “подводные камни” Вы заметили в экзамене?
    много разных вариантов определения какого-то термина
    ну и надо знать все подходы, я бы сказал так
    если вы прочитали книгу, вы сможете без проблем сдать экзамен
  • Какова стоимость сдачи экзамена в Украине?
    125 евро
  • Как проходит регистрация, и к кому обращаться? (Александр Тарасенко редко реагирует на письма/звонки)
    именно к Тарасенко и нужно
    но в общем, начальный уровень можно сдать в любом prometric центре
    так что можно не заморачиваться на счет тарасенка
    https://www.isqi.org/en/e-exams.html
  • В каком виде выдаётся сертификат?
    в бумажном, приходят на почту
  • И по литературе – какие книги Вы бы посоветовали? 
    FOUNDATIONS OF SOFTWARE TESTING, ISTQB CERTIFICATION, Dorothy Graham, Erik van Veenendaal, Isabel Evans, Rex Black (2 книги 2008 и 2012 годов)
    Advanced Software Testing, Rex Black (3 книги)
  • Какого рода вопросы для foundation level?
    Вопросы, которые подразумевают выбор одного или несколько вариантов правильных ответов из перечисленных вариантов.
  • Я прочитала на Вашем сайте, что вы сдали ISTQB Advanced Test Analyst экзамен.
    Какими материалами Вы пользовались при подготовке кроме силлабуса и стандартной книги?
    Остались ли у Вас ссылки, если да, то поделитесь, пожалуйста.
    Я пользовался стандартными книгами Advanced Testing Vol1 и Advanced Testing Vol2 и Advanced Testing Vol3 автор Rex Black.
    К сожалению, одного силлабуса не хватит для того, чтобы подготовиться к advance уровню.
  • ISTQB Advanced Test Analyst экзамен. Я знаю, что в экзаменационном тесте  возможны несколько вариантов правильных ответов. Есть ли в вопросе точное указание на их количество или они перечислены в одном пункте?
    Да некоторые вопросы могут содержать 1 или более правильных ответов, но об этом нигде не написано и не обозначено. Т.е. это будет вопросы с несколькими ответами и Вам надо будет отметить один или более правильных ответов. Но все таки в некоторых вопросах есть упоминание: “вычеркните все которые … ” или “укажите все правильные варианты …”. В общем, надо просто быть внимательным к мелочам и деталям, а также пользоваться здравой логикой.
  • ISTQB Advanced Test Analyst экзамен. Я тоже недавно сдавала этот экзамен, но не сдала. Буду пересдавать. При подготовке я пользовалась силлабусом и стандартной книгой. Но в тесте я встречала термины, о которых ничего не сказано в книге. Так что я думаю, что нужны другие материалы для подготовки. 
    Да силлабус и книги не гарантируют, что Вы будете знать все термины. Я вам так скажу, если Вы прочитаете все три книги, которые я указал, то сдавать будет проще, потому что одна книга дополняют другую. Плюс надо учитывать, что экзамены обновляются постоянно, а книги выпущены все один раз и там некоторая информация уже может быть устаревшей или же в авторы экзамена могли поменять смысл чего-либо. Потому тут так, читайте все литературу и проходите как можно больше заданий. Интернет вам поможет. Есть также круг людей, которые собираются и решают задачи вместе, а есть даже люди, которые учат, как сдать на этот сертификат.
  • ISTQB Advanced Test Analyst экзамен. И последний вопрос, знаете ли вы как начисляются баллы. Если в вопросе два ответа, а я выбрала только один, то сколько баллов будет за такой ответ?
    Я точной схемы начисления баллов не помню. Это нужно уточнять на месте. Если кто-то знает, буду рад добавить текст сюда.
    Updated: Я еще раз переспросила на счет кол-ва правильных ответов в rstqb. Вот что мне ответили “По умолчанию верный только один ответ, если не сказано иного. За верный ответ на вопросы уровня K2 начисляется 1 балл, на вопросы уровней K3 и K4 – по 2 балла”

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

Что еще можно почитать?

http://automated-testing.info/forum/….istqb-ctfl-ctal
http://automated-testing.info/forum/…-istqb

Меня комментируют

Igor Vlasuyk

|

“Продолжительность 16 часов (2 дня)”
Это то есть 2 рабочих дня надо полностью быть на тренинге?
И на каком языке программирования будет практика?

Nataliia Koval

|

Большое спасибо! С расписанием вроде разобралась.

Chmel Viktor

|

Доброго дня

Nataliia Koval

|

Подскажите пожалуйста, после регистрации на сайте pearsonvue.com, как оплатить и выбрать дату и место сдачи? Там есть адреса центров сдачи экзамена. Звонить в эти центры?

Mykhailo Poliarush

|

так и есть, ошибочка, уже поправил

Что вас интересует на этом сайте?

View Results

Loading ... Loading ...