SlideShare a Scribd company logo
1 of 62
para tus pruebas
unitarias
ANTONIO ROBRES TURÓN
Quien soy?
 Test Architect en SCRM (Lidl Group)
 11 Años experiencia en Software Testing
 10 años experiencia en Automation Testing
 Python
 Java
 Javascript
 Ruby
 Retrogamer y amante de los comics
Agenda
 Introduction
 Unit Testing
 Test Doubles
 Mockito
 Using mock in high level components
Una test unitario es una forma de comprobar el correcto
funcionamiento de una unidad funcional de código de forma
aislada
Características Test Unitario
 Automático
 Predecible
 Enfocado
 Rápido
 Aislado
 Repetible
Estructura Test Unitario
 Arrange
 Parte donde se configura e inicializa el Test unitario
 Act
 Es la parte donde se ejecuta el código del Test unitario
 Assert
 Es la parte donde se valida el resultado del Test unitario
Buenas prácticas
 Independencia entre tests
 Nombre descriptivo de los tests
 Cada test solo prueba una cosa
 Implementar Test Doubles para las dependencias
Test Doubles
Tipos de Test Doubles
Mocking es el proceso por el cual el test decide la
implementación y comportamiento que tendrán las
dependencias de la clase que se va a probar.
Porque hacer mocks?
Elimina dependencias en el componente que estamos probando
Permiten testear métodos o funciones que no retornan ningún valor
permite poder probar errores
Reduce la complejidad de los tests (unitarios, de componente o de integración)
Posibilita desarrollar la clase sin esperar a que el resto de componentes estén acabados.
Dummies
Son dobles de test usados como
parámetros obligatorios de un
elemento con los que no se quiere
interactuar.
Suelen ser objetos vacíos ya que
no se van a utilizar directamente
en el test.
Fake
Son implementaciones de
componentes funcionales y
operativas pero con lo
mínimo necesario para
realizar los tests.
Los tests no tienen control
sobre estos.
Mock
Son dobles de tests que son
capaces de analizar la relación
entre el componente que
estamos probando y sus
interacciones.
Nos ayuda a saber el análisis
del comportamiento del
componente que estamos
probando.
Spy
Se utilizan también para
analizar la relación entre el
componente que estamos
probando y sus
interacciones pero sin
sustituir al objeto real.
Stub
 Es un componente que tiene
un comportamiento
predefinido y que se utiliza
para responder a las llamadas
del componente que estamos
probando.
 Este componente sustituye al
objeto real
Dummy example
class SuperPower():
name = None
def __init__(self, name):
self.name = name
def use(self):
print('Super Power Used')
class SuperHero:
first_name = None
second_name = None
def __init__(self, first_name, second_name, super_power):
self.first_name = first_name
self.second_name = second_name
self.super_power = super_power
def get_first_name(self):
return self.first_name
def get_second_name(self):
return self.second_name
def get_full_name(self):
return self.first_name + ' ' + self.second_name
def attack(self):
self.super_power.use()
import pytest
from unittest.mock import sentinel
from src.dummy import SuperHero
def test_super_hero():
hero = SuperHero(first_name='Doctor', second_name='Strange' ,
super_power=sentinel)
print(hero.get_full_name())
Lo que une el desarrollo que no lo
separen las pruebas
Problema
 Necesitamos un método que retorne a un candidato aleatorio:
 No puedes ser tu mismo
 No puedes ser alguien que hayas visto anteriormente
Función a testear
import random
users = ['Sara', 'Alexandra']
def get_next_person(user):
person = get_random_user()
while person in user['people_seen']:
person = get_random_user()
return person
def get_random_user():
return random.choice(users)
Unit Test
from src.next_person import get_next_person
def test_new_person():
# Arrange
user = {'people_seen': []}
expected_person = 'Alexandra'
# Action
actual_person = get_next_person(user)
# Assert
assert actual_person == expected_person
from src.next_person import get_next_person
from mock import patch
@patch("src.next_person.get_random_user")
def test_new_person(mock_get_random_user):
# Arrange
user = {'people_seen': []}
expected_person = 'Alexandra'
mock_get_random_user.return_value = 'Alexandra'
# Action
actual_person = get_next_person(user)
# Assert
assert actual_person == expected_person
Otra manera
def test_new_person():
# Arrange
app = TindevQA()
user = {'people_seen': []}
expected_person = 'Alexandra'
app.get_random_user = Mock()
app.get_random_user.return_value = 'Alexandra'
# Action
actual_person = app.get_next_person(user)
# Assert
assert actual_person == expected_person
Context Manager
def test_new_person_context_manager():
with patch.object(TindevQA, "get_random_user") as mock_get_random_user:
# Arrange
app = TindevQA()
user = {'people_seen': []}
expected_person = 'Alexandra'
mock_get_random_user.return_value = 'Alexandra'
# Action
actual_person = app.get_next_person(user)
# Assert
assert actual_person == expected_person
Varias respuestas
@patch.object(TindevQA, "get_random_user")
def test_new_person_several_times(mock_get_random_user):
app = TindevQA()
user = {'people_seen': ['Sara', 'Alexandra']}
expected_person = 'Leia'
mock_get_random_user.side_effect = ['Alexandra', 'Sara', 'Leia']
# Action
actual_person = app.get_next_person(user)
# Assert
assert actual_person == expected_person
Problema TinDevQA Match
 Cuando el usuario le gusta a una persona…
 Si el otro usuario le gusta
 Mandar un mail a ambos usuarios
 Cuando el otro usuario no le gusta
 Mandar un mail de no me gusta de forma civilizada
 Si el otro usuario no lo ha evaluado
 Decir que espere que el otro usuario le evalue
Evaluar Match
def evaluate(self, person1, person2):
if person1 in person2['likes']:
self.send_email(person1)
self.send_email(person2)
elif person1 in person2['dislikes']:
self.let_down_gently(person1)
elif person1 not in person2['likes']
and person1 not in person2['dislikes']:
self.give_it_time(person1)
NO RETURNS!!!!!
Comprobar que se ha llamado el
método
@patch.object(TindevQA, "let_down_gently")
def test_person2_dislikes_person1(mock_let_down):
app = TindevQA()
person1 = 'Luke'
person2 = {'name': 'Leia',
'likes': ['Han Solo'],
'dislikes': ['Luke']}
app.evaluate(person1, person2)
assert mock_let_down.call_count == 1
Comprobar los parametros
@patch.object(TindevQA, "let_down_gently")
def test_person2_dislikes_person1_parameters(mock_let_down):
app = TindevQA()
person1 = 'Luke'
person2 = {'name': 'Leia',
'likes': ['Han Solo'],
'dislikes': ['Luke']}
app.evaluate(person1, person2)
mock_let_down.assert_called_once_with(person1)
Multiple mocks
@patch.object(TindevQA, "give_it_time")
@patch.object(TindevQA, "send_email")
@patch.object(TindevQA, "let_down_gently")
def test_person2_dislikes_person1_multiple_mocks(mock_let_down, mock_send_mail,
mock_give_it_time):
app = TindevQA()
person1 = 'Luke'
person2 = {'name': 'Leia',
'likes': ['Han Solo'],
'dislikes': ['Luke']}
app.evaluate(person1, person2)
mock_let_down.assert_called_once_with(person1)
assert mock_send_mail.call_count == 0
assert mock_give_it_time.call_count == 0
Multiple calls
@patch.object(TindevQA, "send_email")
def test_person2_likes_person1( mock_send_mail):
app = TindevQA()
person1 = 'Han Solo'
person2 = {'name': 'Leia',
'likes': ['Han Solo'],
'dislikes': ['Luke']}
app.evaluate(person1, person2)
person1_mail_received = mock_send_mail.call_args_list[0]
person2_mail_received = mock_send_mail.call_args_list[1]
assert person1_mail_received == call(person1)
assert person2_mail_received == call(person2)
Mocking Exceptions and builtins
def get_information_from_file(self, filename):
try:
return json.loads(open(filename).read())
except (IOError, ValueError):
return "ERROR"
Testing JSON reader
 Test parseo de un fichero valido
 Test de una exception IOError
 Test de una exception ValueError
@patch("builtins.open")
def test_get_valid_json(mock_open):
app = TindevQA()
filename = "configuration.json"
expected_result = {'name': 'Leia', 'likes': ['Han Solo'],
'dislikes': ['Luke']}
mock_file = Mock()
mock_file.read.return_value = '{"name": "Leia", "likes": ["Han Solo"],
"dislikes": ["Luke"]}'
mock_open.return_value = mock_file
actual_result = app.get_information_from_file(filename)
assert actual_result == expected_result
Error Handling
@patch("builtins.open")
def test_get_information_ioerror(mock_open):
app = TindevQA()
filename = "configuration.json"
mock_open.side_effect = IOError
actual_result = app.get_information_from_file(filename)
assert actual_result == "ERROR"
Error Handling
@patch("json.loads")
@patch("builtins.open")
def test_get_information_valueerror(mock_open, mock_loads):
app = TindevQA()
filename = "configuration.json"
mock_file = Mock()
mock_file.read.return_value = '{"name": "Leia", "likes": ["Han Solo"],
"dislikes": ["Luke"]}'
mock_open.return_value = mock_file
mock_loads.side_effect = ValueError
actual_result = app.get_information_from_file(filename)
assert actual_result == "ERROR"
When… ThenReturn
def test_new_person_decorator():
# Arrange
app = TindevQA()
user = {'people_seen': []}
expected_person = 'Alexandra'
when(app).get_random_user().thenReturn('Alexandra')
# Action
actual_person = app.get_next_person(user)
# Assert
assert actual_person == expected_person
Multiples Respuestas
def test_new_person_several_times():
app = TindevQA()
user = {'people_seen': ['Sara', 'Alexandra']}
expected_person = 'Leia'
when(app).get_random_user().thenReturn('Alexandra', 'Sara', 'Leia')
# Action
actual_person = app.get_next_person(user)
# Assert
assert actual_person == expected_person
Validar que se ha llamado
def test_person2_dislikes_person1():
app = TindevQA()
person1 = 'Luke'
person2 = {'name': 'Leia',
'likes': ['Han Solo'],
'dislikes': ['Luke']}
when(app).let_down_gently(...)
app.evaluate(person1, person2)
verify(app, times=1).let_down_gently(...)
Validando los parámetros
def test_person2_dislikes_person1_parameters():
app = TindevQA()
person1 = 'Luke'
person2 = {'name': 'Leia',
'likes': ['Han Solo'],
'dislikes': ['Luke']}
when(app).let_down_gently(person1)
app.evaluate(person1, person2)
verify(app, times=1).let_down_gently(person1)
Multiple Mocks
def test_person2_dislikes_person1_multiple_mocks():
app = TindevQA()
person1 = 'Luke'
person2 = {'name': 'Leia',
'likes': ['Han Solo'],
'dislikes': ['Luke']}
when(app).let_down_gently(person1)
when(app).send_email(...)
when(app).give_it_time(...)
app.evaluate(person1, person2)
verify(app, times=1).let_down_gently(person1)
verify(app, times=0).send_email(...)
verify(app, times=0).give_it_time(...)
Multiple Calls
def test_person2_likes_person1():
app = TindevQA()
person1 = 'Han Solo'
person2 = {'name': 'Leia',
'likes': ['Han Solo'],
'dislikes': ['Luke']}
when(app).send_email(...)
app.evaluate(person1, person2)
verify(app, times=2).send_email(...)
verify(app, times=1).send_email(person1)
verify(app, times=1).send_email(person2)
Error Handling
def test_get_information_ioerror():
app = TindevQA()
filename = "configuration.json"
when(builtins.open(filename)).read().thenRaise(IOError)
actual_result = app.get_information_from_file(filename)
assert actual_result == "ERROR"
Integration Test
def send_sms(self, user, text, to):
try:
body = {'from': user, 'text': text, 'to': to,
'api_key': ‘XXXXXX', 'api_secret: ‘XXXXXXXXX'}
r = requests.post(url=URL, data=body)
return r
except Timeout:
return "ERROR"
Mandar SMS a un usuario
Integration Test
def test_send_sms():
app = TindevQA()
user = 'Chewbacca'
text = 'Quieres enredarte entre mis pelos?'
to = 34600000000
response = app.send_sms(user, text, to)
assert response.ok == True
Integration test Mocking
def test_send_sms_with_mockito():
app = TindevQA()
user = 'Chewbacca'
text = 'Quieres enredarte entre mis pelos?'
response_mock = mock({'status_code': 200, 'text': 'Ok'})
when(requests).post(...).thenReturn(response_mock)
response = app.send_sms(user, text)
assert response.status_code == 200
Y si no tengo acceso al código?
Create tu propio mock
@app.post("/sms/json")
@app.post("/sms/json")
def send_sms():
body = {'from': request.forms.get("from"),
'text': request.forms.get("text"),
'to': request.forms.get("to")}
requests_handled.append(body)
if len(response_saved) > 0:
response_to_send = response_saved.pop()
response.status = response_to_send['status_code']
return response_to_send['message']
Funciones para “espiar” y devolver
respuestas predefinidas
@app.post("/set_response")
def set_response():
body = b"".join(request.body)
body = json.loads(body)
response_saved.append(body)
@app.get("/stats")
def stats():
body = {'requests': list(requests_handled)}
return json.dumps(body)
Test usando nuestro propio mock
def test_send_sms():
app = TindevQA()
user = 'Chewbacca'
text = 'Quieres enredarte entre mis pelos?'
response = app.send_sms(user, text)
r = requests.get('http://localhost:8080/stats')
body = r.json()
assert body['requests'][0]['from'] == 'Chewbacca'
assert body['requests'][0]['to'] == ‘34000000000'
assert body['requests'][0]['text'] == 'Quieres enredarte entre mis pelos?'
Test forzando una respuesta errónea
def test_send_sms_with_error():
app = TindevQA()
user = 'Chewbacca'
text = 'Quieres enredarte entre mis pelos?'
data = {'status_code': 500, 'message': 'Servidor Caido'}
r = requests.post('http://localhost:8080/set_response', json=data)
response = app.send_sms(user, text)
assert response.status_code == 500
assert response.text == 'Servidor Caido'
Resumen
 Los mocks nos ayudan a escribir tests unitarios más
simples
 Eliminan dependencias
 Verifican comportamiento de métodos sin retorno
 Nos permiten testear errores
 Nos permiten testear más rápido
 Nos permiten testear sin que el resto de componentes
estén finalizados
Referencias
 https://docs.python.org/3/library/unittest.mock.html
 https://martinfowler.com/articles/practical-test-pyramid.html
 https://martinfowler.com/articles/mocksArentStubs.html
 http://xunitpatterns.com/Test%20Double.html
 http://blog.cleancoder.com/uncle-bob/2014/05/10/WhenToMock.html
 https://bottlepy.org/docs/dev/
 https://github.com/twiindan/mocking
 https://mockito-python.readthedocs.io/en/latest/
Preguntas?
Email: twiindan@gmail.com
Twitter: @twiindan
Linkedin: https://www.linkedin.com/in/antoniorobres

More Related Content

What's hot

05. 아키텍트가 알아야할 12 97가지
05. 아키텍트가 알아야할 12 97가지05. 아키텍트가 알아야할 12 97가지
05. 아키텍트가 알아야할 12 97가지
YoungSu Son
 

What's hot (20)

Nest.js Introduction
Nest.js IntroductionNest.js Introduction
Nest.js Introduction
 
Clean backends with NestJs
Clean backends with NestJsClean backends with NestJs
Clean backends with NestJs
 
Java presentation
Java presentation Java presentation
Java presentation
 
Unit testing & TDD concepts with best practice guidelines.
Unit testing & TDD concepts with best practice guidelines.Unit testing & TDD concepts with best practice guidelines.
Unit testing & TDD concepts with best practice guidelines.
 
Angular Interview Questions & Answers
Angular Interview Questions & AnswersAngular Interview Questions & Answers
Angular Interview Questions & Answers
 
Clean architecture
Clean architectureClean architecture
Clean architecture
 
PHP Unit Testing in Yii
PHP Unit Testing in YiiPHP Unit Testing in Yii
PHP Unit Testing in Yii
 
Laravel Introduction
Laravel IntroductionLaravel Introduction
Laravel Introduction
 
Agile Test Driven Development
Agile Test Driven DevelopmentAgile Test Driven Development
Agile Test Driven Development
 
iOS Automation: XCUITest + Gherkin
iOS Automation: XCUITest + GherkiniOS Automation: XCUITest + Gherkin
iOS Automation: XCUITest + Gherkin
 
Spring I/O 2012: Natural Templating in Spring MVC with Thymeleaf
Spring I/O 2012: Natural Templating in Spring MVC with ThymeleafSpring I/O 2012: Natural Templating in Spring MVC with Thymeleaf
Spring I/O 2012: Natural Templating in Spring MVC with Thymeleaf
 
The Enterprise Case for Node.js
The Enterprise Case for Node.jsThe Enterprise Case for Node.js
The Enterprise Case for Node.js
 
.Net Core
.Net Core.Net Core
.Net Core
 
Object Oriented Design Principles
Object Oriented Design PrinciplesObject Oriented Design Principles
Object Oriented Design Principles
 
Angular 8
Angular 8 Angular 8
Angular 8
 
Discover Quarkus and GraalVM
Discover Quarkus and GraalVMDiscover Quarkus and GraalVM
Discover Quarkus and GraalVM
 
Domain Driven Design and Hexagonal Architecture with Rails
Domain Driven Design and Hexagonal Architecture with RailsDomain Driven Design and Hexagonal Architecture with Rails
Domain Driven Design and Hexagonal Architecture with Rails
 
Testing ppt
Testing pptTesting ppt
Testing ppt
 
SOLID Design Principles
SOLID Design PrinciplesSOLID Design Principles
SOLID Design Principles
 
05. 아키텍트가 알아야할 12 97가지
05. 아키텍트가 알아야할 12 97가지05. 아키텍트가 알아야할 12 97가지
05. 아키텍트가 알아야할 12 97가지
 

Similar to Mockito para tus pruebas unitarias

Concurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STM
Concurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STMConcurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STM
Concurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STM
Mario Fusco
 

Similar to Mockito para tus pruebas unitarias (20)

Test doubles
Test doublesTest doubles
Test doubles
 
Grails unit testing
Grails unit testingGrails unit testing
Grails unit testing
 
Unit/Integration Testing using Spock
Unit/Integration Testing using SpockUnit/Integration Testing using Spock
Unit/Integration Testing using Spock
 
Unit testing
Unit testingUnit testing
Unit testing
 
TDD & BDD
TDD & BDDTDD & BDD
TDD & BDD
 
Practical Celery
Practical CeleryPractical Celery
Practical Celery
 
How To Test Everything
How To Test EverythingHow To Test Everything
How To Test Everything
 
Why Our Code Smells
Why Our Code SmellsWhy Our Code Smells
Why Our Code Smells
 
Testing Legacy Rails Apps
Testing Legacy Rails AppsTesting Legacy Rails Apps
Testing Legacy Rails Apps
 
Unit test-using-spock in Grails
Unit test-using-spock in GrailsUnit test-using-spock in Grails
Unit test-using-spock in Grails
 
Java performance
Java performanceJava performance
Java performance
 
Javascript Objects Deep Dive
Javascript Objects Deep DiveJavascript Objects Deep Dive
Javascript Objects Deep Dive
 
Easy mock
Easy mockEasy mock
Easy mock
 
Easymock Tutorial
Easymock TutorialEasymock Tutorial
Easymock Tutorial
 
Akka Testkit Patterns
Akka Testkit PatternsAkka Testkit Patterns
Akka Testkit Patterns
 
Oojs 1.1
Oojs 1.1Oojs 1.1
Oojs 1.1
 
Concurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STM
Concurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STMConcurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STM
Concurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STM
 
Spring hibernate jsf_primefaces_intergration
Spring hibernate jsf_primefaces_intergrationSpring hibernate jsf_primefaces_intergration
Spring hibernate jsf_primefaces_intergration
 
Testing And Drupal
Testing And DrupalTesting And Drupal
Testing And Drupal
 
Paying off technical debt with PHPSpec
Paying off technical debt with PHPSpecPaying off technical debt with PHPSpec
Paying off technical debt with PHPSpec
 

More from Antonio Robres Turon (10)

¡El mejor lenguaje para automatizar pruebas!
¡El mejor lenguaje para automatizar pruebas!¡El mejor lenguaje para automatizar pruebas!
¡El mejor lenguaje para automatizar pruebas!
 
[Tefcon] are you ready for the war
[Tefcon] are you ready for the war[Tefcon] are you ready for the war
[Tefcon] are you ready for the war
 
BDD en practica (seminario)
BDD en practica (seminario)BDD en practica (seminario)
BDD en practica (seminario)
 
El pequeño se hace mayor
El pequeño se hace mayor El pequeño se hace mayor
El pequeño se hace mayor
 
Testing Testing everywhere
Testing Testing everywhereTesting Testing everywhere
Testing Testing everywhere
 
One to rule them all
One to rule them allOne to rule them all
One to rule them all
 
Hello bdd
Hello bddHello bdd
Hello bdd
 
Testing Proud
Testing ProudTesting Proud
Testing Proud
 
Existe el tester perfecto
Existe el tester perfectoExiste el tester perfecto
Existe el tester perfecto
 
Existe el tester perfecto
Existe el tester perfectoExiste el tester perfecto
Existe el tester perfecto
 

Recently uploaded

Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Medical / Health Care (+971588192166) Mifepristone and Misoprostol tablets 200mg
 

Recently uploaded (20)

WSO2CON 2024 - Architecting AI in the Enterprise: APIs and Applications
WSO2CON 2024 - Architecting AI in the Enterprise: APIs and ApplicationsWSO2CON 2024 - Architecting AI in the Enterprise: APIs and Applications
WSO2CON 2024 - Architecting AI in the Enterprise: APIs and Applications
 
WSO2CON 2024 - Designing Event-Driven Enterprises: Stories of Transformation
WSO2CON 2024 - Designing Event-Driven Enterprises: Stories of TransformationWSO2CON 2024 - Designing Event-Driven Enterprises: Stories of Transformation
WSO2CON 2024 - Designing Event-Driven Enterprises: Stories of Transformation
 
WSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaSWSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaS
 
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
 
WSO2CON 2024 - How CSI Piemonte Is Apifying the Public Administration
WSO2CON 2024 - How CSI Piemonte Is Apifying the Public AdministrationWSO2CON 2024 - How CSI Piemonte Is Apifying the Public Administration
WSO2CON 2024 - How CSI Piemonte Is Apifying the Public Administration
 
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
 
WSO2Con2024 - Organization Management: The Revolution in B2B CIAM
WSO2Con2024 - Organization Management: The Revolution in B2B CIAMWSO2Con2024 - Organization Management: The Revolution in B2B CIAM
WSO2Con2024 - Organization Management: The Revolution in B2B CIAM
 
WSO2Con2024 - Facilitating Broadband Switching Services for UK Telecoms Provi...
WSO2Con2024 - Facilitating Broadband Switching Services for UK Telecoms Provi...WSO2Con2024 - Facilitating Broadband Switching Services for UK Telecoms Provi...
WSO2Con2024 - Facilitating Broadband Switching Services for UK Telecoms Provi...
 
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
 
Artyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptxArtyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptx
 
WSO2CON2024 - Why Should You Consider Ballerina for Your Next Integration
WSO2CON2024 - Why Should You Consider Ballerina for Your Next IntegrationWSO2CON2024 - Why Should You Consider Ballerina for Your Next Integration
WSO2CON2024 - Why Should You Consider Ballerina for Your Next Integration
 
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
 
Architecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastArchitecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the past
 
WSO2CON 2024 - Unlocking the Identity: Embracing CIAM 2.0 for a Competitive A...
WSO2CON 2024 - Unlocking the Identity: Embracing CIAM 2.0 for a Competitive A...WSO2CON 2024 - Unlocking the Identity: Embracing CIAM 2.0 for a Competitive A...
WSO2CON 2024 - Unlocking the Identity: Embracing CIAM 2.0 for a Competitive A...
 
WSO2CON 2024 - Software Engineering for Digital Businesses
WSO2CON 2024 - Software Engineering for Digital BusinessesWSO2CON 2024 - Software Engineering for Digital Businesses
WSO2CON 2024 - Software Engineering for Digital Businesses
 
WSO2Con2024 - GitOps in Action: Navigating Application Deployment in the Plat...
WSO2Con2024 - GitOps in Action: Navigating Application Deployment in the Plat...WSO2Con2024 - GitOps in Action: Navigating Application Deployment in the Plat...
WSO2Con2024 - GitOps in Action: Navigating Application Deployment in the Plat...
 
WSO2CON 2024 - Building a Digital Government in Uganda
WSO2CON 2024 - Building a Digital Government in UgandaWSO2CON 2024 - Building a Digital Government in Uganda
WSO2CON 2024 - Building a Digital Government in Uganda
 
WSO2Con2024 - Simplified Integration: Unveiling the Latest Features in WSO2 L...
WSO2Con2024 - Simplified Integration: Unveiling the Latest Features in WSO2 L...WSO2Con2024 - Simplified Integration: Unveiling the Latest Features in WSO2 L...
WSO2Con2024 - Simplified Integration: Unveiling the Latest Features in WSO2 L...
 
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
 
WSO2CON 2024 - How CSI Piemonte Is Apifying the Public Administration
WSO2CON 2024 - How CSI Piemonte Is Apifying the Public AdministrationWSO2CON 2024 - How CSI Piemonte Is Apifying the Public Administration
WSO2CON 2024 - How CSI Piemonte Is Apifying the Public Administration
 

Mockito para tus pruebas unitarias

  • 2. Quien soy?  Test Architect en SCRM (Lidl Group)  11 Años experiencia en Software Testing  10 años experiencia en Automation Testing  Python  Java  Javascript  Ruby  Retrogamer y amante de los comics
  • 3. Agenda  Introduction  Unit Testing  Test Doubles  Mockito  Using mock in high level components
  • 4.
  • 5.
  • 6.
  • 7. Una test unitario es una forma de comprobar el correcto funcionamiento de una unidad funcional de código de forma aislada
  • 8. Características Test Unitario  Automático  Predecible  Enfocado  Rápido  Aislado  Repetible
  • 9. Estructura Test Unitario  Arrange  Parte donde se configura e inicializa el Test unitario  Act  Es la parte donde se ejecuta el código del Test unitario  Assert  Es la parte donde se valida el resultado del Test unitario
  • 10. Buenas prácticas  Independencia entre tests  Nombre descriptivo de los tests  Cada test solo prueba una cosa  Implementar Test Doubles para las dependencias
  • 12. Tipos de Test Doubles
  • 13. Mocking es el proceso por el cual el test decide la implementación y comportamiento que tendrán las dependencias de la clase que se va a probar.
  • 14. Porque hacer mocks? Elimina dependencias en el componente que estamos probando Permiten testear métodos o funciones que no retornan ningún valor permite poder probar errores Reduce la complejidad de los tests (unitarios, de componente o de integración) Posibilita desarrollar la clase sin esperar a que el resto de componentes estén acabados.
  • 15. Dummies Son dobles de test usados como parámetros obligatorios de un elemento con los que no se quiere interactuar. Suelen ser objetos vacíos ya que no se van a utilizar directamente en el test.
  • 16. Fake Son implementaciones de componentes funcionales y operativas pero con lo mínimo necesario para realizar los tests. Los tests no tienen control sobre estos.
  • 17. Mock Son dobles de tests que son capaces de analizar la relación entre el componente que estamos probando y sus interacciones. Nos ayuda a saber el análisis del comportamiento del componente que estamos probando.
  • 18. Spy Se utilizan también para analizar la relación entre el componente que estamos probando y sus interacciones pero sin sustituir al objeto real.
  • 19. Stub  Es un componente que tiene un comportamiento predefinido y que se utiliza para responder a las llamadas del componente que estamos probando.  Este componente sustituye al objeto real
  • 20.
  • 21. Dummy example class SuperPower(): name = None def __init__(self, name): self.name = name def use(self): print('Super Power Used') class SuperHero: first_name = None second_name = None def __init__(self, first_name, second_name, super_power): self.first_name = first_name self.second_name = second_name self.super_power = super_power def get_first_name(self): return self.first_name def get_second_name(self): return self.second_name def get_full_name(self): return self.first_name + ' ' + self.second_name def attack(self): self.super_power.use() import pytest from unittest.mock import sentinel from src.dummy import SuperHero def test_super_hero(): hero = SuperHero(first_name='Doctor', second_name='Strange' , super_power=sentinel) print(hero.get_full_name())
  • 22. Lo que une el desarrollo que no lo separen las pruebas
  • 23. Problema  Necesitamos un método que retorne a un candidato aleatorio:  No puedes ser tu mismo  No puedes ser alguien que hayas visto anteriormente
  • 24. Función a testear import random users = ['Sara', 'Alexandra'] def get_next_person(user): person = get_random_user() while person in user['people_seen']: person = get_random_user() return person def get_random_user(): return random.choice(users)
  • 25. Unit Test from src.next_person import get_next_person def test_new_person(): # Arrange user = {'people_seen': []} expected_person = 'Alexandra' # Action actual_person = get_next_person(user) # Assert assert actual_person == expected_person
  • 26.
  • 27.
  • 28.
  • 29. from src.next_person import get_next_person from mock import patch @patch("src.next_person.get_random_user") def test_new_person(mock_get_random_user): # Arrange user = {'people_seen': []} expected_person = 'Alexandra' mock_get_random_user.return_value = 'Alexandra' # Action actual_person = get_next_person(user) # Assert assert actual_person == expected_person
  • 30. Otra manera def test_new_person(): # Arrange app = TindevQA() user = {'people_seen': []} expected_person = 'Alexandra' app.get_random_user = Mock() app.get_random_user.return_value = 'Alexandra' # Action actual_person = app.get_next_person(user) # Assert assert actual_person == expected_person
  • 31. Context Manager def test_new_person_context_manager(): with patch.object(TindevQA, "get_random_user") as mock_get_random_user: # Arrange app = TindevQA() user = {'people_seen': []} expected_person = 'Alexandra' mock_get_random_user.return_value = 'Alexandra' # Action actual_person = app.get_next_person(user) # Assert assert actual_person == expected_person
  • 32. Varias respuestas @patch.object(TindevQA, "get_random_user") def test_new_person_several_times(mock_get_random_user): app = TindevQA() user = {'people_seen': ['Sara', 'Alexandra']} expected_person = 'Leia' mock_get_random_user.side_effect = ['Alexandra', 'Sara', 'Leia'] # Action actual_person = app.get_next_person(user) # Assert assert actual_person == expected_person
  • 33. Problema TinDevQA Match  Cuando el usuario le gusta a una persona…  Si el otro usuario le gusta  Mandar un mail a ambos usuarios  Cuando el otro usuario no le gusta  Mandar un mail de no me gusta de forma civilizada  Si el otro usuario no lo ha evaluado  Decir que espere que el otro usuario le evalue
  • 34. Evaluar Match def evaluate(self, person1, person2): if person1 in person2['likes']: self.send_email(person1) self.send_email(person2) elif person1 in person2['dislikes']: self.let_down_gently(person1) elif person1 not in person2['likes'] and person1 not in person2['dislikes']: self.give_it_time(person1) NO RETURNS!!!!!
  • 35. Comprobar que se ha llamado el método @patch.object(TindevQA, "let_down_gently") def test_person2_dislikes_person1(mock_let_down): app = TindevQA() person1 = 'Luke' person2 = {'name': 'Leia', 'likes': ['Han Solo'], 'dislikes': ['Luke']} app.evaluate(person1, person2) assert mock_let_down.call_count == 1
  • 36. Comprobar los parametros @patch.object(TindevQA, "let_down_gently") def test_person2_dislikes_person1_parameters(mock_let_down): app = TindevQA() person1 = 'Luke' person2 = {'name': 'Leia', 'likes': ['Han Solo'], 'dislikes': ['Luke']} app.evaluate(person1, person2) mock_let_down.assert_called_once_with(person1)
  • 37. Multiple mocks @patch.object(TindevQA, "give_it_time") @patch.object(TindevQA, "send_email") @patch.object(TindevQA, "let_down_gently") def test_person2_dislikes_person1_multiple_mocks(mock_let_down, mock_send_mail, mock_give_it_time): app = TindevQA() person1 = 'Luke' person2 = {'name': 'Leia', 'likes': ['Han Solo'], 'dislikes': ['Luke']} app.evaluate(person1, person2) mock_let_down.assert_called_once_with(person1) assert mock_send_mail.call_count == 0 assert mock_give_it_time.call_count == 0
  • 38. Multiple calls @patch.object(TindevQA, "send_email") def test_person2_likes_person1( mock_send_mail): app = TindevQA() person1 = 'Han Solo' person2 = {'name': 'Leia', 'likes': ['Han Solo'], 'dislikes': ['Luke']} app.evaluate(person1, person2) person1_mail_received = mock_send_mail.call_args_list[0] person2_mail_received = mock_send_mail.call_args_list[1] assert person1_mail_received == call(person1) assert person2_mail_received == call(person2)
  • 39. Mocking Exceptions and builtins def get_information_from_file(self, filename): try: return json.loads(open(filename).read()) except (IOError, ValueError): return "ERROR"
  • 40. Testing JSON reader  Test parseo de un fichero valido  Test de una exception IOError  Test de una exception ValueError
  • 41. @patch("builtins.open") def test_get_valid_json(mock_open): app = TindevQA() filename = "configuration.json" expected_result = {'name': 'Leia', 'likes': ['Han Solo'], 'dislikes': ['Luke']} mock_file = Mock() mock_file.read.return_value = '{"name": "Leia", "likes": ["Han Solo"], "dislikes": ["Luke"]}' mock_open.return_value = mock_file actual_result = app.get_information_from_file(filename) assert actual_result == expected_result
  • 42. Error Handling @patch("builtins.open") def test_get_information_ioerror(mock_open): app = TindevQA() filename = "configuration.json" mock_open.side_effect = IOError actual_result = app.get_information_from_file(filename) assert actual_result == "ERROR"
  • 43. Error Handling @patch("json.loads") @patch("builtins.open") def test_get_information_valueerror(mock_open, mock_loads): app = TindevQA() filename = "configuration.json" mock_file = Mock() mock_file.read.return_value = '{"name": "Leia", "likes": ["Han Solo"], "dislikes": ["Luke"]}' mock_open.return_value = mock_file mock_loads.side_effect = ValueError actual_result = app.get_information_from_file(filename) assert actual_result == "ERROR"
  • 44.
  • 45. When… ThenReturn def test_new_person_decorator(): # Arrange app = TindevQA() user = {'people_seen': []} expected_person = 'Alexandra' when(app).get_random_user().thenReturn('Alexandra') # Action actual_person = app.get_next_person(user) # Assert assert actual_person == expected_person
  • 46. Multiples Respuestas def test_new_person_several_times(): app = TindevQA() user = {'people_seen': ['Sara', 'Alexandra']} expected_person = 'Leia' when(app).get_random_user().thenReturn('Alexandra', 'Sara', 'Leia') # Action actual_person = app.get_next_person(user) # Assert assert actual_person == expected_person
  • 47. Validar que se ha llamado def test_person2_dislikes_person1(): app = TindevQA() person1 = 'Luke' person2 = {'name': 'Leia', 'likes': ['Han Solo'], 'dislikes': ['Luke']} when(app).let_down_gently(...) app.evaluate(person1, person2) verify(app, times=1).let_down_gently(...)
  • 48. Validando los parámetros def test_person2_dislikes_person1_parameters(): app = TindevQA() person1 = 'Luke' person2 = {'name': 'Leia', 'likes': ['Han Solo'], 'dislikes': ['Luke']} when(app).let_down_gently(person1) app.evaluate(person1, person2) verify(app, times=1).let_down_gently(person1)
  • 49. Multiple Mocks def test_person2_dislikes_person1_multiple_mocks(): app = TindevQA() person1 = 'Luke' person2 = {'name': 'Leia', 'likes': ['Han Solo'], 'dislikes': ['Luke']} when(app).let_down_gently(person1) when(app).send_email(...) when(app).give_it_time(...) app.evaluate(person1, person2) verify(app, times=1).let_down_gently(person1) verify(app, times=0).send_email(...) verify(app, times=0).give_it_time(...)
  • 50. Multiple Calls def test_person2_likes_person1(): app = TindevQA() person1 = 'Han Solo' person2 = {'name': 'Leia', 'likes': ['Han Solo'], 'dislikes': ['Luke']} when(app).send_email(...) app.evaluate(person1, person2) verify(app, times=2).send_email(...) verify(app, times=1).send_email(person1) verify(app, times=1).send_email(person2)
  • 51. Error Handling def test_get_information_ioerror(): app = TindevQA() filename = "configuration.json" when(builtins.open(filename)).read().thenRaise(IOError) actual_result = app.get_information_from_file(filename) assert actual_result == "ERROR"
  • 52. Integration Test def send_sms(self, user, text, to): try: body = {'from': user, 'text': text, 'to': to, 'api_key': ‘XXXXXX', 'api_secret: ‘XXXXXXXXX'} r = requests.post(url=URL, data=body) return r except Timeout: return "ERROR" Mandar SMS a un usuario
  • 53. Integration Test def test_send_sms(): app = TindevQA() user = 'Chewbacca' text = 'Quieres enredarte entre mis pelos?' to = 34600000000 response = app.send_sms(user, text, to) assert response.ok == True
  • 54. Integration test Mocking def test_send_sms_with_mockito(): app = TindevQA() user = 'Chewbacca' text = 'Quieres enredarte entre mis pelos?' response_mock = mock({'status_code': 200, 'text': 'Ok'}) when(requests).post(...).thenReturn(response_mock) response = app.send_sms(user, text) assert response.status_code == 200
  • 55. Y si no tengo acceso al código?
  • 56. Create tu propio mock @app.post("/sms/json") @app.post("/sms/json") def send_sms(): body = {'from': request.forms.get("from"), 'text': request.forms.get("text"), 'to': request.forms.get("to")} requests_handled.append(body) if len(response_saved) > 0: response_to_send = response_saved.pop() response.status = response_to_send['status_code'] return response_to_send['message']
  • 57. Funciones para “espiar” y devolver respuestas predefinidas @app.post("/set_response") def set_response(): body = b"".join(request.body) body = json.loads(body) response_saved.append(body) @app.get("/stats") def stats(): body = {'requests': list(requests_handled)} return json.dumps(body)
  • 58. Test usando nuestro propio mock def test_send_sms(): app = TindevQA() user = 'Chewbacca' text = 'Quieres enredarte entre mis pelos?' response = app.send_sms(user, text) r = requests.get('http://localhost:8080/stats') body = r.json() assert body['requests'][0]['from'] == 'Chewbacca' assert body['requests'][0]['to'] == ‘34000000000' assert body['requests'][0]['text'] == 'Quieres enredarte entre mis pelos?'
  • 59. Test forzando una respuesta errónea def test_send_sms_with_error(): app = TindevQA() user = 'Chewbacca' text = 'Quieres enredarte entre mis pelos?' data = {'status_code': 500, 'message': 'Servidor Caido'} r = requests.post('http://localhost:8080/set_response', json=data) response = app.send_sms(user, text) assert response.status_code == 500 assert response.text == 'Servidor Caido'
  • 60. Resumen  Los mocks nos ayudan a escribir tests unitarios más simples  Eliminan dependencias  Verifican comportamiento de métodos sin retorno  Nos permiten testear errores  Nos permiten testear más rápido  Nos permiten testear sin que el resto de componentes estén finalizados
  • 61. Referencias  https://docs.python.org/3/library/unittest.mock.html  https://martinfowler.com/articles/practical-test-pyramid.html  https://martinfowler.com/articles/mocksArentStubs.html  http://xunitpatterns.com/Test%20Double.html  http://blog.cleancoder.com/uncle-bob/2014/05/10/WhenToMock.html  https://bottlepy.org/docs/dev/  https://github.com/twiindan/mocking  https://mockito-python.readthedocs.io/en/latest/
  • 62. Preguntas? Email: twiindan@gmail.com Twitter: @twiindan Linkedin: https://www.linkedin.com/in/antoniorobres

Editor's Notes

  1. El assert se ejecuta sobre el mock (State Testing e Interaction Testing)
  2. El tests tiene el control sobre este test double