Diferència entre revisions de la pàgina «Introducció al testing»
Salta a la navegació
Salta a la cerca
(Es crea la pàgina amb «= Primera presa de contacte = == unittest == # 00_testing_unittest.py import unittest # Funció que volem provar def suma(a, b): return a + b # Classe de...».) |
|||
(Hi ha 15 revisions intermèdies del mateix usuari que no es mostren) | |||
Línia 1: | Línia 1: | ||
= Primera presa de contacte = | = Primera presa de contacte = | ||
+ | [https://recull.binefa.cat/files/doc/testing/ Codis de testing] | ||
+ | * Per a certificar-se: | ||
+ | [https://www.sstqb.com/downloads ISTQB® Certified Tester Foundation Level] | ||
== unittest == | == unittest == | ||
+ | Codi: | ||
# 00_testing_unittest.py | # 00_testing_unittest.py | ||
import unittest | import unittest | ||
Línia 25: | Línia 29: | ||
if __name__ == '__main__': | if __name__ == '__main__': | ||
unittest.main() | unittest.main() | ||
+ | Execució de la prova: | ||
+ | python 00_testing_unittest.py | ||
+ | Sortida pel terminal: | ||
+ | .... | ||
+ | ---------------------------------------------------------------------- | ||
+ | Ran 4 tests in 0.000s | ||
+ | |||
+ | OK | ||
+ | Forçant un error. Per exemple: | ||
+ | self.assertEqual(suma(-1, -1), '''-3''') | ||
+ | La sortida al terminal seria: | ||
+ | .F.. | ||
+ | ====================================================================== | ||
+ | FAIL: test_suma_negatius (__main__.TestSuma) | ||
+ | ---------------------------------------------------------------------- | ||
+ | Traceback (most recent call last): | ||
+ | File "/media/emmagatzematge/curs2024-2025/dam/testing/codis/00_testing_unittest.py", line 14, in test_suma_negatius | ||
+ | self.assertEqual(suma(-1, -1), -3) | ||
+ | AssertionError: -2 != -3 | ||
+ | |||
+ | ---------------------------------------------------------------------- | ||
+ | Ran 4 tests in 0.001s | ||
+ | |||
+ | FAILED (failures=1) | ||
+ | |||
== pytest == | == pytest == | ||
Heu d'instal·lar ''pytest'' | Heu d'instal·lar ''pytest'' | ||
Línia 65: | Línia 94: | ||
============================== 4 passed in 0.03s =============================== | ============================== 4 passed in 0.03s =============================== | ||
+ | Forçant un error. Per exemple: | ||
+ | assert multiplica(5, -2) == -11 | ||
+ | Sortiria pel terminal: | ||
+ | pytest 01_testing_pytest.py -v | ||
+ | |||
+ | ============================= test session starts ============================== | ||
+ | platform linux -- Python 3.9.2, pytest-8.3.5, pluggy-1.5.0 -- /usr/bin/python3 | ||
+ | cachedir: .pytest_cache | ||
+ | rootdir: /media/emmagatzematge/curs2024-2025/dam/testing/codis | ||
+ | plugins: anyio-3.3.2 | ||
+ | collected 4 items | ||
+ | |||
+ | 01_testing_pytest.py::test_multiplica_positius PASSED [ 25%] | ||
+ | 01_testing_pytest.py::test_multiplica_negatius PASSED [ 50%] | ||
+ | 01_testing_pytest.py::test_multiplica_mixta FAILED [ 75%] | ||
+ | 01_testing_pytest.py::test_multiplica_zero PASSED [100%] | ||
+ | |||
+ | =================================== FAILURES =================================== | ||
+ | ____________________________ test_multiplica_mixta _____________________________ | ||
+ | |||
+ | def test_multiplica_mixta(): | ||
+ | > assert multiplica(5, -2) == -11 | ||
+ | E assert -10 == -11 | ||
+ | E + where -10 = multiplica(5, -2) | ||
+ | |||
+ | 01_testing_pytest.py:15: AssertionError | ||
+ | =========================== short test summary info ============================ | ||
+ | FAILED 01_testing_pytest.py::test_multiplica_mixta - assert -10 == -11 | ||
+ | ========================= 1 failed, 3 passed in 0.10s ========================== | ||
+ | |||
+ | = Comprovant múltiples valors. Prova parametritzada = | ||
+ | Codi: | ||
+ | # 02_testing_pytest.py | ||
+ | |||
+ | import pytest | ||
+ | |||
+ | def multiplica(a, b): | ||
+ | return a * b | ||
+ | |||
+ | @pytest.mark.parametrize("a,b,esperat", [ | ||
+ | (3, 4, 12), # Correcte | ||
+ | (-2, -3, 5), # Incorrecte (hauria de ser 6) | ||
+ | (5, -2, -10), # Correcte | ||
+ | (0, 100, 1) # Incorrecte (hauria de ser 0) | ||
+ | ]) | ||
+ | def test_multiplica_parametritzada(a, b, esperat): | ||
+ | assert multiplica(a, b) == esperat | ||
+ | Observeu la sortida al terminal al fer el test: | ||
+ | pytest 02_testing_pytest.py -v | ||
+ | = Exemple amb fixtures (dades de prova reutilitzables) en pytest = | ||
+ | Codi: | ||
+ | # 03_testing_fixtures_pytest.py | ||
+ | import pytest | ||
+ | |||
+ | # Definim una fixture (dades de prova) | ||
+ | @pytest.fixture | ||
+ | def llista_nums(): | ||
+ | return [1, 2, 3, 4, 5] | ||
+ | |||
+ | # Test que utilitza la fixture | ||
+ | def test_suma_llista(llista_nums): | ||
+ | assert sum(llista_nums) == 15 | ||
+ | |||
+ | # Test que comprova la longitud | ||
+ | def test_llargada_llista(llista_nums): | ||
+ | assert len(llista_nums) == 5 | ||
+ | = Testeig d'excepcions = | ||
+ | Codi: | ||
+ | # 04_testing_excepcions_pytest.py | ||
+ | import pytest | ||
+ | |||
+ | def divideix(a, b): | ||
+ | if b == 0: | ||
+ | raise ValueError("No es pot dividir per zero!") | ||
+ | return a / b | ||
+ | |||
+ | def test_divideix(): | ||
+ | assert divideix(10, 2) == 5 | ||
+ | |||
+ | def test_divideix_per_zero(): | ||
+ | with pytest.raises(ValueError) as excinfo: | ||
+ | divideix(5, 0) | ||
+ | assert "No es pot dividir per zero!" in str(excinfo.value) | ||
+ | |||
+ | = Exemples de testing amb QtTest a les Qt en C++ = | ||
+ | == testcalculator == | ||
+ | * [https://recull.binefa.cat/files/doc/testing/testcalculator.zip Codis del projecte testcalculator] | ||
+ | // testcalculator.cpp | ||
+ | #include <QtTest> | ||
+ | #include "calculator.h" | ||
+ | |||
+ | class TestCalculator : public QObject | ||
+ | { | ||
+ | Q_OBJECT | ||
+ | |||
+ | private slots: | ||
+ | void testSum() | ||
+ | { | ||
+ | Calculator calc; | ||
+ | QCOMPARE(calc.sum(2, 3), 5); | ||
+ | QCOMPARE(calc.sum(-1, 1), 0); | ||
+ | } | ||
+ | |||
+ | void testMultiply() | ||
+ | { | ||
+ | Calculator calc; | ||
+ | QCOMPARE(calc.multiply(3, 4), 12); | ||
+ | QCOMPARE(calc.multiply(-2, -3), 6); // Forçant error canviant a 5 | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | QTEST_APPLESS_MAIN(TestCalculator) // Sense interfície gràfica. Amb GUI està QTEST_MAIN | ||
+ | #include "testcalculator.moc" | ||
+ | == Test unitari == | ||
+ | * [https://recull.binefa.cat/files/doc/testing/00_testUnitari.zip Test unitari] proposat al [https://doc.qt.io/qt-6/qtest-tutorial.html Qt Test Tutorial] | ||
+ | - testqstring.h: | ||
+ | #ifndef TESTQSTRING_H | ||
+ | #define TESTQSTRING_H | ||
+ | |||
+ | #include <QTest> | ||
+ | |||
+ | class TestQString: public QObject | ||
+ | { | ||
+ | Q_OBJECT | ||
+ | private slots: | ||
+ | void toUpper(); | ||
+ | }; | ||
+ | |||
+ | #endif // TESTQSTRING_H | ||
+ | - testqstring.cpp: | ||
+ | #include "testqstring.h" | ||
+ | |||
+ | void TestQString::toUpper() | ||
+ | { | ||
+ | QString str = "Hello"; | ||
+ | // QVERIFY(str.toUpper() == "HELLO"); | ||
+ | QCOMPARE(str.toUpper(), QString("HELLO")); | ||
+ | } | ||
+ | |||
+ | QTEST_MAIN(TestQString) | ||
+ | |||
+ | = Exemple de testing amb QtTest a QML = | ||
+ | [https://recull.binefa.cat/files/doc/testing/test02.zip Codi del projecte test02] | ||
+ | import QtQuick 2.15 | ||
+ | import QtQuick.Controls 2.15 | ||
+ | import '''QtTest''' 1.15 | ||
+ | ... | ||
+ | '''TestCase''' { | ||
+ | id: testCase | ||
+ | name: "MathFunctionsTest" | ||
+ | when: false // Desactivem l'execució automàtica | ||
+ | |||
+ | // Propietat per emmagatzemar resultats | ||
+ | property string resultsString: "" | ||
+ | |||
+ | function runAllTests() { | ||
+ | resultsString = ""; | ||
+ | var tests = ["test_addNumbers", "test_multiplyNumbers"]; | ||
+ | |||
+ | for (var i = 0; i < tests.length; i++) { | ||
+ | var testName = tests[i]; | ||
+ | try { | ||
+ | this[testName](); | ||
+ | resultsString += "✓ PASS: " + testName + "\n"; | ||
+ | console.log("PASS:", testName); | ||
+ | } catch (e) { | ||
+ | resultsString += "✗ FAIL: " + testName + " - " + e + "\n"; | ||
+ | console.error("FAIL:", testName, "-", e); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | statusLabel.text = "Tests completats!\n" + resultsString; | ||
+ | } | ||
+ | |||
+ | function '''test_'''addNumbers() { | ||
+ | compare(addNumbers(2, 3), 5); | ||
+ | compare(addNumbers(-1, -1), -2); | ||
+ | } | ||
+ | |||
+ | function '''test_'''multiplyNumbers() { | ||
+ | compare(multiply(3, 4), 12); | ||
+ | compare(multiply(-2, -3), 5); // Error deliberat | ||
+ | } | ||
+ | |||
+ | function addNumbers(a, b) { | ||
+ | return a + b; | ||
+ | } | ||
+ | |||
+ | function multiply(a, b) { | ||
+ | return a * b; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | = Documentació sobre testing = | ||
+ | * [https://www.geeksforgeeks.org/software-testing-tutorial/ Software Testing Tutorial] | ||
+ | * [https://doc.qt.io/qt-6/qttest-index.html Qt Test] | ||
+ | * [https://doc.qt.io/qt-6/qtest-tutorial.html Qt Test Tutorial] |
Revisió de 12:12, 4 abr 2025
Contingut
Primera presa de contacte
- Per a certificar-se:
ISTQB® Certified Tester Foundation Level
unittest
Codi:
# 00_testing_unittest.py import unittest # Funció que volem provar def suma(a, b): return a + b # Classe de tests class TestSuma(unittest.TestCase): def test_suma_positius(self): self.assertEqual(suma(2, 3), 5) def test_suma_negatius(self): self.assertEqual(suma(-1, -1), -2) def test_suma_mixta(self): self.assertEqual(suma(5, -3), 2) def test_suma_zero(self): self.assertEqual(suma(0, 0), 0) # Executar els tests si s'executa directament aquest fitxer if __name__ == '__main__': unittest.main()
Execució de la prova:
python 00_testing_unittest.py
Sortida pel terminal:
.... ---------------------------------------------------------------------- Ran 4 tests in 0.000s OK
Forçant un error. Per exemple:
self.assertEqual(suma(-1, -1), -3)
La sortida al terminal seria:
.F.. ====================================================================== FAIL: test_suma_negatius (__main__.TestSuma) ---------------------------------------------------------------------- Traceback (most recent call last): File "/media/emmagatzematge/curs2024-2025/dam/testing/codis/00_testing_unittest.py", line 14, in test_suma_negatius self.assertEqual(suma(-1, -1), -3) AssertionError: -2 != -3 ---------------------------------------------------------------------- Ran 4 tests in 0.001s FAILED (failures=1)
pytest
Heu d'instal·lar pytest
pip install pytest
Si sou a Linux cal afegir la ruta de pytest al PATH. Per exemple, afegint al final de l'arxiu .basrc de la carpeta d'usuari (canvieu jordi pel vostre usuari al sistema):
export PATH=$PATH:/home/jordi/.local/bin
Codi d'exemple:
# 01_testing_pytest.py # Funció que volem provar def multiplica(a, b): return a * b # Tests amb pytest (només cal definir funcions que comencin per "test_") def test_multiplica_positius(): assert multiplica(3, 4) == 12 def test_multiplica_negatius(): assert multiplica(-2, -3) == 6 def test_multiplica_mixta(): assert multiplica(5, -2) == -10 def test_multiplica_zero(): assert multiplica(0, 100) == 0
Execució:
pytest 01_testing_pytest.py -v
Sortida pel terminal:
============================= test session starts ============================== platform linux -- Python 3.9.2, pytest-8.3.5, pluggy-1.5.0 -- /usr/bin/python3 cachedir: .pytest_cache rootdir: /media/emmagatzematge/curs2024-2025/dam/testing/codis plugins: anyio-3.3.2 collected 4 items 01_testing_pytest.py::test_multiplica_positius PASSED [ 25%] 01_testing_pytest.py::test_multiplica_negatius PASSED [ 50%] 01_testing_pytest.py::test_multiplica_mixta PASSED [ 75%] 01_testing_pytest.py::test_multiplica_zero PASSED [100%] ============================== 4 passed in 0.03s ===============================
Forçant un error. Per exemple:
assert multiplica(5, -2) == -11
Sortiria pel terminal:
pytest 01_testing_pytest.py -v
============================= test session starts ============================== platform linux -- Python 3.9.2, pytest-8.3.5, pluggy-1.5.0 -- /usr/bin/python3 cachedir: .pytest_cache rootdir: /media/emmagatzematge/curs2024-2025/dam/testing/codis plugins: anyio-3.3.2 collected 4 items 01_testing_pytest.py::test_multiplica_positius PASSED [ 25%] 01_testing_pytest.py::test_multiplica_negatius PASSED [ 50%] 01_testing_pytest.py::test_multiplica_mixta FAILED [ 75%] 01_testing_pytest.py::test_multiplica_zero PASSED [100%] =================================== FAILURES =================================== ____________________________ test_multiplica_mixta _____________________________ def test_multiplica_mixta(): > assert multiplica(5, -2) == -11 E assert -10 == -11 E + where -10 = multiplica(5, -2) 01_testing_pytest.py:15: AssertionError =========================== short test summary info ============================ FAILED 01_testing_pytest.py::test_multiplica_mixta - assert -10 == -11 ========================= 1 failed, 3 passed in 0.10s ==========================
Comprovant múltiples valors. Prova parametritzada
Codi:
# 02_testing_pytest.py import pytest def multiplica(a, b): return a * b @pytest.mark.parametrize("a,b,esperat", [ (3, 4, 12), # Correcte (-2, -3, 5), # Incorrecte (hauria de ser 6) (5, -2, -10), # Correcte (0, 100, 1) # Incorrecte (hauria de ser 0) ]) def test_multiplica_parametritzada(a, b, esperat): assert multiplica(a, b) == esperat
Observeu la sortida al terminal al fer el test:
pytest 02_testing_pytest.py -v
Exemple amb fixtures (dades de prova reutilitzables) en pytest
Codi:
# 03_testing_fixtures_pytest.py import pytest # Definim una fixture (dades de prova) @pytest.fixture def llista_nums(): return [1, 2, 3, 4, 5] # Test que utilitza la fixture def test_suma_llista(llista_nums): assert sum(llista_nums) == 15 # Test que comprova la longitud def test_llargada_llista(llista_nums): assert len(llista_nums) == 5
Testeig d'excepcions
Codi:
# 04_testing_excepcions_pytest.py import pytest def divideix(a, b): if b == 0: raise ValueError("No es pot dividir per zero!") return a / b def test_divideix(): assert divideix(10, 2) == 5 def test_divideix_per_zero(): with pytest.raises(ValueError) as excinfo: divideix(5, 0) assert "No es pot dividir per zero!" in str(excinfo.value)
Exemples de testing amb QtTest a les Qt en C++
testcalculator
// testcalculator.cpp #include <QtTest> #include "calculator.h" class TestCalculator : public QObject { Q_OBJECT private slots: void testSum() { Calculator calc; QCOMPARE(calc.sum(2, 3), 5); QCOMPARE(calc.sum(-1, 1), 0); } void testMultiply() { Calculator calc; QCOMPARE(calc.multiply(3, 4), 12); QCOMPARE(calc.multiply(-2, -3), 6); // Forçant error canviant a 5 } }; QTEST_APPLESS_MAIN(TestCalculator) // Sense interfície gràfica. Amb GUI està QTEST_MAIN #include "testcalculator.moc"
Test unitari
- Test unitari proposat al Qt Test Tutorial
- testqstring.h:
#ifndef TESTQSTRING_H #define TESTQSTRING_H #include <QTest> class TestQString: public QObject { Q_OBJECT private slots: void toUpper(); }; #endif // TESTQSTRING_H
- testqstring.cpp:
#include "testqstring.h" void TestQString::toUpper() { QString str = "Hello"; // QVERIFY(str.toUpper() == "HELLO"); QCOMPARE(str.toUpper(), QString("HELLO")); } QTEST_MAIN(TestQString)
Exemple de testing amb QtTest a QML
import QtQuick 2.15 import QtQuick.Controls 2.15 import QtTest 1.15 ... TestCase { id: testCase name: "MathFunctionsTest" when: false // Desactivem l'execució automàtica // Propietat per emmagatzemar resultats property string resultsString: "" function runAllTests() { resultsString = ""; var tests = ["test_addNumbers", "test_multiplyNumbers"]; for (var i = 0; i < tests.length; i++) { var testName = tests[i]; try { this[testName](); resultsString += "✓ PASS: " + testName + "\n"; console.log("PASS:", testName); } catch (e) { resultsString += "✗ FAIL: " + testName + " - " + e + "\n"; console.error("FAIL:", testName, "-", e); } } statusLabel.text = "Tests completats!\n" + resultsString; } function test_addNumbers() { compare(addNumbers(2, 3), 5); compare(addNumbers(-1, -1), -2); } function test_multiplyNumbers() { compare(multiply(3, 4), 12); compare(multiply(-2, -3), 5); // Error deliberat } function addNumbers(a, b) { return a + b; } function multiply(a, b) { return a * b; } }