Diferència entre revisions de la pàgina «Introducció al testing»

De binefa.com
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

Primera presa de contacte

Codis de testing

  • 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

- 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

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