You don't have access to this class

Keep learning! Join and start boosting your career

Aprovecha el precio especial y haz tu profesi贸n a prueba de IA

Antes: $249

Currency
$209
Suscr铆bete

Termina en:

1 D铆as
7 Hrs
45 Min
2 Seg

Control de pruebas unitarias con unittest.skip en Python

8/20
Resources

In software development, it is common to face situations where unit tests cannot be executed due to changes or ongoing development. In these cases, commenting out test code is not the best practice. Fortunately, Python and unittest offer decorators that allow us to temporarily skip tests without compromising workflow and project integrity. Here we will learn how to use decorators like @skip, @skipIf and @expectedFailure to handle these cases efficiently.

How to use the @skip decorator?

The @skip decorator is used when we know that a test should not be executed temporarily. This is useful if we are working on a feature that is not yet complete and, therefore, the tests do not make sense. By applying @skip, we can avoid executing the test and still have visibility that it is pending fixes.

  • We apply the decorator with a clear reason.
  • When we run the tests, the report will indicate that the test was skipped.

Example of use:

@unittest.skip("Work in progress, will be re-enabled.")def test_skip_example(self):self.assertEqual("hello", "bye").

When to apply @skipIf?

The @skipIf decorator is useful when we want to skip a test under a specific condition. This is common when our tests depend on the environment, such as different servers or specific configurations.

  • It requires a condition and a reason to be applied.
  • It will be executed only if the condition is true.

Example usage:

server = "server_b"@unittest.skipIf(server == "server_a", "Skipped because we are not on the correct server.")def test_skipif_example(self):self.assertEqual(1000, 100)

What does the @expectedFailure decorator do?

This decorator is used when we know a test will fail due to a change in business logic or a known bug, but we want to keep the test visible in the test report.

  • It is useful to reflect expected failures without interfering with the continuous integration flow.
  • The report will show that the test failed as expected.

Example usage:

@unittest.expectedFailuredef test_expected_failure_example(self):self.assertEqual(100, 150).

How to apply @skipUnless in advanced cases?

The @skipUnless decorator is valuable when we want to run a test only if a condition is met. A classic example is to validate if an external service, such as an API, is available before running the test.

  • It is ideal for scenarios where we rely on external resources, such as third-party API's.

Example usage:

@unittest.skipUnless(api_available(), "API not available.")def test_skipunless_example(self):self.assertEqual(get_currency_rate("USD"), 1.0)

When to use these decorators in collaborative development?

Using decorators like @skip, @skipIf, @expectedFailure and @skipUnless in a development team ensures that tests do not interfere with the workflow, while maintaining visibility of pending tests. It is essential in continuous integration (CI) environments, where you want tests to not block development, but not ignore them completely.

Contributions 6

Questions 1

Sort by:

Want to see more contributions, questions and answers from the community?

Me ayud茅 un poco de ChatGPT para hacer la solicitud de la API, porque nunca hab铆a consumido una. Estuve buscando API's que dieran el tipo de cambio y me encontr茅 con que el Banco de M茅xico tiene una. Con base en ella realic茅 la prueba. Se gener贸 una funci贸n para conseguir el tipo de cambio y otra para definir si la API est谩 disponible. ```python # ----- C贸digo de las funciones ----- import requests #Permite hacer solicitudes HTTP # Se utiliza la API del Banco de M茅xico para hacer el cambio de moneda. # Con la siguiente funci贸n se obtiene desde la API de Banxico el tipo de cambio: def get_exchange_rate(api): url = f'https://www.banxico.org.mx/SieAPIRest/service/v1/series/SF63528/datos/oportuno?token={api}' response = requests.get(url) if response.status_code == 200: data = response.json() return float(data['bmx']['series'][0]['datos'][0]['dato']) else: print('Error to obtain exchange rate:', response.status_code) return None # Con la siguiente funci贸n se verifica si la API est谩 dispoible: def is_api_available(api): url = f'https://www.banxico.org.mx/SieAPIRest/service/v1/series/SF63528/datos/oportuno?token={api}' response = requests.get(url) return response.status_code == 200 # Aqu铆 comienza el c贸digo para solucionar los retos de la clase class BankAccount: def __init__(self, balance=0, log_file=None): self.balance = balance self.log_file = log_file self._log_transaction('Cuenta creada') def _log_transaction(self, message): if self.log_file: with open(self.log_file, "a") as f: f.write(f"{message}\n") def deposit(self, amount): if amount > 0: self.balance += amount self._log_transaction(f"Deposited {amount}. New balance: {self.balance}.") return self.balance def withdraw(self, amount): if amount > 0: self.balance -= amount self._log_transaction(f"Withdrew {amount}. New balance: {self.balance}.") return self.balance def get_balance(self): self._log_transaction(f"Checked balance. Current balance {self.balance}.") return self.balance def transfer(self, amount): if self.balance >= amount: self.balance = self.balance - amount self._log_transaction(f"Transfered {amount}. New balance: {self.balance}.") else: self._log_transaction(f"Not transfered {amount}. Insufficient founds: {self.balance}") print("Fondos insuficientes.") return self.balance # Adicional a lo trabajado en las clases anteriores, con la siguiente funci贸n podremos convertir a USD: def convert_to_usd(self, api): exchange_rate = get_exchange_rate(api) if exchange_rate: return self.balance / exchange_rate else: return None # ----- C贸digo de las pruebas ----- import unittest import os from src.bank_account import BankAccount from src.bank_account import is_api_available # Se importa funci贸n que se agreg贸 fuera de la clse from unittest.mock import patch class BankAccountTests(unittest.TestCase): # Con setUp lo que se hace es definir desde el inicio los valores que tendr谩n las funciones. # Es una manera de evitar escribir los valores iniciales desde el inicio, en este caso,todas las variables iniciar谩n con $1,000 # def setUp(self) -> None: self.account = BankAccount(balance=1000, log_file="transaction_log.txt") # Aqu铆 se define la API key: self.api = 'e3980208bf01ec653aba9aee3c2d6f70f6ae8b066d2545e379b9e0ef92e9de25' def tearDown(self) -> None: if os.path.exists(self.account.log_file): os.remove(self.account.log_file) def _count_lines(self, filename): with open(filename, "r") as f: return len(f.readlines()) def test_deposit(self): new_balance = self.account.deposit(500) self.assertEqual(new_balance, 1500, "El balance no es igual") def test_withdraw(self): new_balance = self.account.withdraw(200) self.assertEqual(new_balance, 800, "El balance no es igual") def test_get_balance(self): self.assertEqual(self.account.get_balance(), 1000, "El balance no es igual") def test_transfer(self): new_balance = self.account.transfer(200) self.assertEqual(new_balance, 800, "El balance no es igual") def test_transaction_log(self): new_balance = self.account.deposit(500) self.assertTrue(os.path.exists("transaction_log.txt")) def test_count_transactions(self): assert self._count_lines(self.account.log_file) == 1 self.account.deposit(500) assert self._count_lines(self.account.log_file) == 2 # Se agrega conversi贸n a USD, condicionada a disponibilidad de API # Se utiliza skipUnless en caso de que la API no est茅 disponible. @unittest.skipUnless(is_api_available('e3980208bf01ec653aba9aee3c2d6f70f6ae8b066d2545e379b9e0ef92e9de25'), 'API no disponible') @patch('src.bank_account.get_exchange_rate') def test_convert_to_usd(self, mock_get_exchange_rate): mock_get_exchange_rate.return_value = 20 # Ejemplo de tipo de cambio usd_balance = self.account.convert_to_usd(self.api) assert usd_balance == 50 # 1000/20 = 50 ```
![](https://static.platzi.com/media/user_upload/python_decorators_skipt_test.drawio-9af73f23-8969-4cae-badf-24a4fed28c70.jpg)
El reto: Lo que hice fue instalar decouple y usar config, cree un archivo .env para evitar que la secret key termine en el repo remoto y use el request de toda la vida con un get. Adem谩s de lo anterior use una variable available que cambia a True si la api esta disponible junto con skipUnless con el mensaje "La API no est谩 disponible para pruebas". skipUnless = <https://www.pythontutorial.net/python-unit-testing/python-unittest-skip-test/> @unittest.skipUnless(condition, reason) ```python import unittest from decouple import config import requests SERVER = "server_b" API_KEY = config('SECRET_KEY') URL = f'https://v6.exchangerate-api.com/v6/{API_KEY}/latest/USD' response = requests.get(URL) available = False if response.status_code == 200: data = response.json() print(data) available = True else: print(f'Error {response.status_code}: {response.text}') class AllAssertsTest(unittest.TestCase): def test_assert_equal(self): self.assertEqual(10, 10) self.assertEqual("Hola", "Hola") def test_assert_true_or_false(self): self.assertTrue(True) self.assertTrue(True) def test_assert_raises(self): with self.assertRaises(ValueError): int("no soy un n煤mero") def test_assert_in(self): self.assertIn(10, [2, 4, 5, 10]) self.assertNotIn(5, [2, 4, 10]) def test_assert_dicts(self): user = {"name": "jesus", "last_name": "vergara"} self.assertDictEqual( {"name": "jesus", "last_name": "vergara"}, user ) self.assertSetEqual( {1, 2, 3}, {1, 2, 3} ) @unittest.skip("Trabajo en progreso, se habilitar谩 nuevamente!") def test_skip(self): self.assertEqual("hola", "chao") @unittest.skipIf(SERVER == "server_b", "Saltado porque no estamos en el servidor a") def test_skip_if(self): self.assertEqual(100, 100) @unittest.expectedFailure def test_expected_failure(self): self.assertEqual(100, 150) @unittest.skipUnless(available, "La API no est谩 disponible para pruebas") def test_api_response(self): self.assertTrue(available, "La API deber铆a estar disponible") if available: print("La API est谩 disponible.") ```import unittestfrom decouple import configimport requests SERVER = "server\_b"API\_KEY = config('SECRET\_KEY') URL = f'https://v6.exchangerate-api.com/v6/{API\_KEY}/latest/USD' response = requests.get(URL)available = Falseif response.status\_code == 200: data = response.json() print(data) available = Trueelse: print(f'Error {response.status\_code}: {response.text}') class AllAssertsTest(unittest.TestCase): def test\_assert\_equal(self): self.assertEqual(10, 10) self.assertEqual("Hola", "Hola") def test\_assert\_true\_or\_false(self): self.assertTrue(True) self.assertTrue(True) def test\_assert\_raises(self): with self.assertRaises(ValueError): int("no soy un n煤mero") def test\_assert\_in(self): self.assertIn(10, \[2, 4, 5, 10]) self.assertNotIn(5, \[2, 4, 10]) def test\_assert\_dicts(self): user = {"name": "jesus", "last\_name": "vergara"} self.assertDictEqual( {"name": "jesus", "last\_name": "vergara"}, user ) self.assertSetEqual( {1, 2, 3}, {1, 2, 3} ) @unittest.skip("Trabajo en progreso, se habilitar谩 nuevamente!") def test\_skip(self): self.assertEqual("hola", "chao") @unittest.skipIf(SERVER == "server\_b", "Saltado porque no estamos en el servidor a") def test\_skip\_if(self): self.assertEqual(100, 100) @unittest.expectedFailure def test\_expected\_failure(self): self.assertEqual(100, 150) @unittest.skipUnless(available, "La API no est谩 disponible para pruebas") def test\_api\_response(self): self.assertTrue(available, "La API deber铆a estar disponible") if available: print("La API est谩 disponible.")
Yo intent茅 solucionar el reto de la manera m谩s simple posible. 1. Cree una API alojada en el local host con un diccionario que contiene un currency del d贸lar fijo, llamada currency\_web.py:![](https://static.platzi.com/media/user_upload/image-e5127511-d5e2-41a3-8829-3cbb05359df4.jpg) 2. Luego, cre茅 otro archivo dentro del m贸dulo de 'src', llamado 'get\_currency.py', en el cual intento extraer el valor del currency 'USD' de la API creada anteriormente. Sin embargo, he usado las sentencias 'try' y 'except' para capturar el error de forma adecuada en caso de que la API no est茅 corriendo![](https://static.platzi.com/media/user_upload/image-64dba36b-d0cd-4e5d-ab60-412ba192b2df.jpg)(por razones de seguridad no he puesto mi ip loca, cada quien debe tener la suya) 3. Finalmente us茅 el decorador 'skipUnless' en el m贸dulo de 'test\_all\_asserts.py'. PRimero establec铆 la variable USD como el valor que me retorna la funci贸n del m贸dulo de 'get\_currency', y dentro de los par谩metros de 'skipUnless' simplemente coloqu茅 debe hacerse la excepci贸n a menos que USD sea un n煤mero, es decir, que la API est茅 corriendo de forma exitosa.![](https://static.platzi.com/media/user_upload/image-85732c39-091b-4911-8ac8-db09c7d0d6d3.jpg)![](https://static.platzi.com/media/user_upload/image-3385a5b7-7f2a-42ee-92dc-a5b61e2a3572.jpg)
Soluci贸n al reto: Ya que era requerido llamar un API, encontr茅 qu茅 [APILayer - Best API Marketplace | Reliable, Scalable APIs | APILayer](https://apilayer.com/marketplace/) tiene un gran repertorio de APIs entre las cuales est谩n para obtener diferentes currencies. Entonces, despu茅s del registro me dieron una *api key* que guard茅 en un archivo <u>.env</u>, para luego inicializarla en la clase ![](https://s13.gifyu.com/images/SGrdl.png) Luego, implement茅 un m茅todo que me permitiera conocer si la *API* est谩 funcionando. ![](https://s7.gifyu.com/images/SGrlM.png) Para que entonces, podamos llamar el m茅todo en nuestros tests que requieran las funcionalidades de la *API*. Por lo tanto, usando el decorador **skipUnless** podemos validar que si la *API* est谩 funcionando, entonces ejecute la prueba, de lo contrario, que muestre el mensaje de error. ![](https://s13.gifyu.com/images/SGrl4.png) Si alg煤n aspecto de la explicaci贸n no qued贸 claro, lo pueden dejar saber : D
Sigo haciendo la comparativa usando `pytest`: ```python # saltar pruebas @pytest.mark.skip def test_deposit_skip(): # Esta prueba ser谩 omitida result = 1000 + 250 assert result == 1250 import sys @pytest.mark.skipif(sys.platform == "win32", reason="No se ejecuta en Windows") def test_withdra_skip(): # Esta prueba solo se ejecutar谩 en plataformas que no sean Windows result = 1000 - 250 assert result == 750 ```