BDD em Python

Me

Gherkin

Given Definição do ambiente
When O teste
Then Validação do teste

Código sendo testado

#!/usr/bin/env python
# -*- encoding: utf-8 -*-

"""This is a sample code that simulates a user being created."""

from __future__ import print_function

import argparse
import json

from collections import Counter
from unicodedata import category

LOWERCASE_CHARS = "Ll"
UPCASE_CHARS = "Lu"
NUMBERS = "Nd"


class UserCreationError(Exception):
    """Base class for all exceptions in this module."""
    pass


class PasswordIsNotStrongEnough(UserCreationError):
    """The password used is not strong enough."""
    pass


class UserAlreadyExists(UserCreationError):
    """The user already exists in the database."""
    pass


def password_is_strong(password):
    """Check if the password have enough strength."""
    number_of = Counter(category(ch) for ch in password)
    return (number_of[LOWERCASE_CHARS] >= 1 and
            number_of[UPCASE_CHARS] >= 2 and
            number_of[NUMBERS] >= 1 and
            len(password) >= 12)


def get_users():
    """Retrieve the list of users in the system."""
    data = {}
    try:
        with open('users.json') as origin:
            data = json.load(origin)
    except IOError:
        # File does not exist, so we just assume it's empty
        pass
    return data


def save_users(user_list):
    """Save the user list back to the 'database'."""
    with open('users.json', 'w') as target:
        json.dump(user_list, target)
    return


def create_user(user, password):
    """'Create' and user."""
    if not password_is_strong(password.decode('utf-8')):
        raise PasswordIsNotStrongEnough

    users = get_users()
    if user in users:
        raise UserAlreadyExists

    users[user] = password
    save_users(users)
    return


def main():
    """Expose the functions back in command line options."""
    parser = argparse.ArgumentParser()
    parser.add_argument('-u', '--user',
                        dest='user',
                        help='Username to be created',
                        required=True)
    parser.add_argument('-p', '--password',
                        dest='password',
                        help='Password for the user',
                        required=True)
    args = parser.parse_args()
    try:
        create_user(args.user, args.password)
    except PasswordIsNotStrongEnough:
        print("That password is not strong enough")
    except UserAlreadyExists:
        print("Username already used")
    else:
        print("User created")
    return


if __name__ == "__main__":
    main()

Robot Framework

pip install robotframework

  • Componentes começam com ***
  • Contém elementos de:
    • Settings: Configuração do ambiente do Robot
    • Keywords: Novas expressões
    • Test Case: Os testes.

Robot Framework


*** Settings ***
Library 	TestingSource.py

*** Test Cases ***
Invalid password
	Given that I have a username	foo
	  And that I have a password	test123
	 When I try to create a user
     Then I should get an error of invalid password

*** Keywords ***
That I have a username 
	[Arguments]		${username}
	set username 	${username}

That I have a password 
	[Arguments]		${password}
	Set password	${password}

I try to create a user
	Create user

I should get an error of invalid password
	Is invalid password
						

Robot Framework

*** Settings ***
Library 	TestingSource.py

Configuração: Indica que deve ser carregado uma biblioteca em Python (a seguir).

Robot Framework

import mainsource


class TestingSource(object):
    def __init__(self):
        self.username = None
        self.password = None
        self.last_exception = None

    def set_username(self, username):
        self.username = username

    def set_password(self, password):
        self.password = password

    def create_user(self):
        try:
            mainsource.create_user(self.username, self.password)
        except mainsource.UserCreationError as exc:
            self.last_exception = exc

    def is_invalid_password(self):
        return (self.last_exception and
                isinstance(self.last_exception,
                           mainsource.PasswordIsNotStrongEnough))

Robot Framework

*** Test Cases ***
Invalid password
    Given that I have a username		foo
      And that I have a password		test123
     When I try to create a user
     Then I should get an error of invalid password

Given, And, When e Then são palavras reservadas.

Parâmetros tem que ser separados com Tabs.

Robot Framework

*** Keywords ***
That I have a username 
	[Arguments]		${username}
	set username 	${username}

That I have a password 
	[Arguments]		${password}
	Set password	${password}

I try to create a user
	Create user

I should get an error of invalid password
	Is invalid password

Robot Framework

$ robot example.robot
 ==============================================================================
 Example
 ==============================================================================
 Invalid password                                                      | PASS |
 ------------------------------------------------------------------------------
 Example                                                               | PASS |
 1 critical test, 1 passed, 0 failed
 1 test total, 1 passed, 0 failed
 ==============================================================================
 Output:  /Users/juliobiason/personal/presentations/_sources/python-bdd/output.xml
 Log:     /Users/juliobiason/personal/presentations/_sources/python-bdd/log.html
 Report:  /Users/juliobiason/personal/presentations/_sources/python-bdd/report.html

Robot Framework

Vantagens:

  • Libraries
  • Por causa dos ***, testes podem estar em vários arquivos -- incluindo RST.

Behave

pip install behave

  • Mais simples
  • Testes ficam em arquivos .feature (tal qual Cucumber)
  • Informações são passadas por context

Behave

Feature: Testing passwords

	Scenario: Invalid password
		Given that I have a username foo
		  And that I have a password test123
		 When I try to create an user
		 Then I should get an error of invalid password

Behave

# -*- encoding: utf-8 -*-

"""Behave steps."""

import behave
import mainsource


@behave.given("that I have a username {name}")
def set_username(context, name):
    context.username = name
    return


@behave.given("that I have a password {password}")
def set_password(context, password):
    context.password = password
    return


@behave.when("I try to create an user")
def create_user(context):
    try:
        mainsource.create_user(context.username, context.password)
    except mainsource.UserCreationError as exc:
        context.last_exception = exc
    return


@behave.then("I should get an error of invalid password")
def is_invalid_password(context):
    assert hasattr(context, 'last_exception')
    assert isinstance(context.last_exception,
                      mainsource.PasswordIsNotStrongEnough)

Behave

Feature: Testing passwords # features/example.feature:1

  Scenario: Invalid password                       # features/example.feature:3
    Given that I have a username foo               # features/steps/example_steps.py:9 0.001s
    And that I have a password test123             # features/steps/example_steps.py:15 0.000s
    When I try to create an user                   # features/steps/example_steps.py:21 0.001s
    Then I should get an error of invalid password # features/steps/example_steps.py:30 0.000s

1 feature passed, 0 failed, 0 skipped
1 scenario passed, 0 failed, 0 skipped
4 steps passed, 0 failed, 0 skipped, 0 undefined
Took 0m0.002s 

Behave

Vantagens:

  • Mais simples

Perguntas?