Pytest - Panduan Cepat

Pytest adalah framework pengujian berbasis python, yang digunakan untuk menulis dan menjalankan kode pengujian. Pada layanan REST saat ini, pytest terutama digunakan untuk pengujian API meskipun kita dapat menggunakan pytest untuk menulis pengujian sederhana hingga kompleks, yaitu, kita dapat menulis kode untuk menguji API, database, UI, dll.

Keuntungan Pytest

Keunggulan Pytest adalah sebagai berikut -

  • Pytest dapat menjalankan beberapa pengujian secara paralel, yang mengurangi waktu eksekusi rangkaian pengujian.

  • Pytest memiliki caranya sendiri untuk mendeteksi file pengujian dan menguji fungsi secara otomatis, jika tidak disebutkan secara eksplisit.

  • Pytest memungkinkan kita melewati subset pengujian selama eksekusi.

  • Pytest memungkinkan kita menjalankan subset dari seluruh rangkaian pengujian.

  • Pytest gratis dan open source.

  • Karena sintaksnya yang sederhana, pytest sangat mudah untuk dimulai.

Dalam tutorial ini, kami akan menjelaskan dasar-dasar pytest dengan program sampel.

Di bab ini, kita akan belajar cara menginstal pytest.

Untuk memulai instalasi, jalankan perintah berikut -

pip install pytest == 2.9.1

Kami dapat menginstal versi pytest apa pun. Di sini, 2.9.1 adalah versi yang kami instal.

Untuk menginstal versi terbaru pytest, jalankan perintah berikut -

pip install pytest

Konfirmasikan penginstalan menggunakan perintah berikut untuk menampilkan bagian bantuan pytest.

pytest -h

Menjalankan pytest tanpa menyebutkan nama file akan menjalankan semua format file test_*.py atau *_test.pydi direktori dan subdirektori saat ini. Pytest secara otomatis mengidentifikasi file tersebut sebagai file pengujian. Kitacan membuat pytest menjalankan nama file lain dengan menyebutkannya secara eksplisit.

Pytest membutuhkan nama fungsi tes untuk memulai test. Nama fungsi yang bukan dalam formattest*tidak dianggap sebagai fungsi pengujian oleh pytest. Kitacannot secara eksplisit membuat pytest mempertimbangkan fungsi apa pun yang tidak dimulai dengan test sebagai fungsi uji.

Kami akan memahami pelaksanaan pengujian di bab-bab berikutnya.

Sekarang, kita akan mulai dengan program pytest pertama kita. Kami pertama-tama akan membuat direktori dan dengan demikian, membuat file uji kami di direktori.

Mari kita ikuti langkah-langkah yang ditunjukkan di bawah ini -

  • Buat direktori baru bernama automation dan masuk ke direktori di baris perintah Anda.

  • Buat file bernama test_square.py dan tambahkan kode di bawah ini ke file itu.

import math

def test_sqrt():
   num = 25
   assert math.sqrt(num) == 5

def testsquare():
   num = 7
   assert 7*7 == 40

def tesequality():
   assert 10 == 11

Jalankan tes menggunakan perintah berikut -

pytest

Perintah di atas akan menghasilkan output berikut -

test_square.py .F
============================================== FAILURES 
==============================================
______________________________________________ testsquare 
_____________________________________________
   def testsquare():
   num=7
>  assert 7*7 == 40
E  assert (7 * 7) == 40
test_square.py:9: AssertionError
================================= 1 failed, 1 passed in 0.06 seconds 
=================================

Lihat hasil baris pertama. Ini menampilkan nama file dan hasilnya. F menunjukkan kegagalan pengujian dan titik (.) Menunjukkan keberhasilan pengujian.

Di bawahnya, kita bisa melihat detail dari tes yang gagal. Ini akan menunjukkan di pernyataan mana tes gagal. Dalam contoh kita, 7 * 7 dibandingkan untuk persamaan dengan 40, yang mana itu salah. Pada akhirnya, kita bisa melihat ringkasan eksekusi uji, 1 gagal dan 1 lolos.

Fungsi tesequality tidak dijalankan karena pytest tidak akan menganggapnya sebagai pengujian karena namanya bukan dalam format test*.

Sekarang, jalankan perintah di bawah ini dan lihat hasilnya lagi -

pytest -v

-v meningkatkan verbositas.

test_square.py::test_sqrt PASSED
test_square.py::testsquare FAILED
============================================== FAILURES 
==============================================
_____________________________________________ testsquare 
_____________________________________________
   def testsquare():
   num = 7
>  assert 7*7 == 40
E  assert (7 * 7) == 40
test_square.py:9: AssertionError
================================= 1 failed, 1 passed in 0.04 seconds 
=================================

Sekarang hasilnya lebih jelas tentang tes yang gagal dan tes yang lulus.

Note - Perintah pytest akan mengeksekusi semua file berformat test_* atau *_test di direktori dan subdirektori saat ini.

Dalam bab ini, kita akan mempelajari cara menjalankan satu file pengujian dan beberapa file pengujian. Kami sudah memiliki file ujitest_square.pydibuat. Buat file tes barutest_compare.py dengan kode berikut -

def test_greater():
   num = 100
   assert num > 100

def test_greater_equal():
   num = 100
   assert num >= 100

def test_less():
   num = 100
   assert num < 200

Sekarang untuk menjalankan semua tes dari semua file (2 file di sini) kita perlu menjalankan perintah berikut -

pytest -v

Perintah di atas akan menjalankan tes dari keduanya test_square.py dan test_compare.py. Outputnya akan dihasilkan sebagai berikut -

test_compare.py::test_greater FAILED
test_compare.py::test_greater_equal PASSED
test_compare.py::test_less PASSED
test_square.py::test_sqrt PASSED
test_square.py::testsquare FAILED
================================================ FAILURES 
================================================
______________________________________________ test_greater 
______________________________________________
   def test_greater():
   num = 100
>  assert num > 100
E  assert 100 > 100

test_compare.py:3: AssertionError
_______________________________________________ testsquare 
_______________________________________________
   def testsquare():
   num = 7
>  assert 7*7 == 40
E  assert (7 * 7) == 40

test_square.py:9: AssertionError
=================================== 2 failed, 3 passed in 0.07 seconds 
===================================

Untuk menjalankan tes dari file tertentu, gunakan sintaks berikut -

pytest <filename> -v

Sekarang, jalankan perintah berikut -

pytest test_compare.py -v

Perintah di atas akan menjalankan tes hanya dari file test_compare.py. Hasil kami akan -

test_compare.py::test_greater FAILED
test_compare.py::test_greater_equal PASSED
test_compare.py::test_less PASSED
============================================== FAILURES 
==============================================
____________________________________________ test_greater 
____________________________________________
   def test_greater():
   num = 100
>  assert num > 100
E  assert 100 > 100
test_compare.py:3: AssertionError
================================= 1 failed, 2 passed in 0.04 seconds 
=================================

Dalam skenario nyata, kami akan memiliki beberapa file pengujian dan setiap file akan memiliki sejumlah pengujian. Tes akan mencakup berbagai modul dan fungsi. Misalkan, kita hanya ingin menjalankan serangkaian pengujian tertentu; bagaimana kita melakukannya?

Pytest menyediakan dua cara untuk menjalankan subset dari rangkaian pengujian.

  • Pilih pengujian untuk dijalankan berdasarkan pencocokan substring dari nama pengujian.
  • Pilih grup pengujian untuk dijalankan berdasarkan penanda yang diterapkan.

Kami akan menjelaskan keduanya dengan contoh di bab-bab selanjutnya.

Untuk menjalankan tes yang mengandung string dalam namanya kita dapat menggunakan sintaks berikut -

pytest -k <substring> -v

-k <substring> mewakili substring yang akan dicari dalam nama pengujian.

Sekarang, jalankan perintah berikut -

pytest -k great -v

Ini akan menjalankan semua nama tes yang memiliki kata tersebut ‘great’dalam namanya. Dalam hal ini, memang demikiantest_greater() dan test_greater_equal(). Lihat hasil di bawah ini.

test_compare.py::test_greater FAILED
test_compare.py::test_greater_equal PASSED
============================================== FAILURES 
==============================================
____________________________________________ test_greater 
____________________________________________
def test_greater():
num = 100
>  assert num > 100
E  assert 100 > 100
test_compare.py:3: AssertionError
========================== 1 failed, 1 passed, 3 deselected in 0.07 seconds 
==========================

Di sini, hasilnya, kita bisa melihat 3 tes yang tidak dipilih. Ini karena nama tes tersebut tidak mengandung katagreat di dalamnya.

Note - Nama fungsi pengujian harus tetap dimulai dengan 'test'.

Dalam bab ini, kita akan belajar bagaimana mengelompokkan tes menggunakan penanda.

Pytest memungkinkan kita menggunakan penanda pada fungsi pengujian. Marker digunakan untuk mengatur berbagai fitur / atribut untuk menguji fungsi. Pytest menyediakan banyak penanda bawaan seperti xfail, skip dan parametrize. Selain itu, pengguna dapat membuat nama penanda sendiri. Penanda diterapkan pada pengujian menggunakan sintaks yang diberikan di bawah ini -

@pytest.mark.<markername>

Untuk menggunakan penanda, kita harus import pytestmodul dalam file pengujian. Kita dapat menentukan nama penanda kita sendiri untuk pengujian dan menjalankan pengujian yang memiliki nama penanda tersebut.

Untuk menjalankan tes yang ditandai, kita dapat menggunakan sintaks berikut -

pytest -m <markername> -v

-m <markername> mewakili nama penanda pengujian yang akan dijalankan.

Perbarui file uji kami test_compare.py dan test_square.pydengan kode berikut. Kami mendefinisikan 3 penanda– great, square, others.

test_compare.py

import pytest
@pytest.mark.great
def test_greater():
   num = 100
   assert num > 100

@pytest.mark.great
def test_greater_equal():
   num = 100
   assert num >= 100

@pytest.mark.others
def test_less():
   num = 100
   assert num < 200

test_square.py

import pytest
import math

@pytest.mark.square
def test_sqrt():
   num = 25
   assert math.sqrt(num) == 5

@pytest.mark.square
def testsquare():
   num = 7
   assert 7*7 == 40

@pytest.mark.others
   def test_equality():
   assert 10 == 11

Sekarang untuk menjalankan tes yang ditandai sebagai others, jalankan perintah berikut -

pytest -m others -v

Lihat hasil di bawah ini. Itu menjalankan 2 tes yang ditandai sebagaiothers.

test_compare.py::test_less PASSED
test_square.py::test_equality FAILED
============================================== FAILURES
==============================================
___________________________________________ test_equality
____________________________________________
   @pytest.mark.others
   def test_equality():
>  assert 10 == 11
E  assert 10 == 11
test_square.py:16: AssertionError
========================== 1 failed, 1 passed, 4 deselected in 0.08 seconds
==========================

Demikian pula, kami juga dapat menjalankan pengujian dengan penanda lain - bagus, bandingkan

Fixtures adalah fungsi yang akan dijalankan sebelum setiap fungsi pengujian yang diterapkan. Perlengkapan digunakan untuk memasukkan beberapa data ke pengujian seperti koneksi database, URL untuk menguji dan beberapa jenis data masukan. Oleh karena itu, alih-alih menjalankan kode yang sama untuk setiap pengujian, kita dapat melampirkan fungsi fixture ke pengujian dan fungsi tersebut akan menjalankan dan mengembalikan data ke pengujian sebelum menjalankan setiap pengujian.

Suatu fungsi ditandai sebagai perlengkapan oleh -

@pytest.fixture

Fungsi uji dapat menggunakan perlengkapan dengan menyebutkan nama perlengkapan sebagai parameter input.

Buat file test_div_by_3_6.py dan tambahkan kode di bawah ini ke dalamnya

import pytest

@pytest.fixture
def input_value():
   input = 39
   return input

def test_divisible_by_3(input_value):
   assert input_value % 3 == 0

def test_divisible_by_6(input_value):
   assert input_value % 6 == 0

Di sini, kami memiliki fungsi perlengkapan bernama input_value, yang memasok input ke pengujian. Untuk mengakses fungsi fixture, tes harus menyebutkan nama fixture sebagai parameter input.

Pytest saat pengujian dijalankan, akan melihat nama fixture sebagai parameter input. Kemudian menjalankan fungsi fixture dan nilai yang dikembalikan disimpan ke parameter input, yang dapat digunakan oleh pengujian.

Jalankan tes menggunakan perintah berikut -

pytest -k divisible -v

Perintah di atas akan menghasilkan hasil sebagai berikut -

test_div_by_3_6.py::test_divisible_by_3 PASSED
test_div_by_3_6.py::test_divisible_by_6 FAILED
============================================== FAILURES
==============================================
________________________________________ test_divisible_by_6
_________________________________________
input_value = 39
   def test_divisible_by_6(input_value):
>  assert input_value % 6 == 0
E  assert (39 % 6) == 0
test_div_by_3_6.py:12: AssertionError
========================== 1 failed, 1 passed, 6 deselected in 0.07 seconds
==========================

Namun, pendekatan tersebut memiliki batasannya sendiri. Fungsi perlengkapan yang ditentukan di dalam file pengujian memiliki cakupan hanya di dalam file pengujian. Kami tidak dapat menggunakan perlengkapan itu di file tes lain. Untuk membuat perlengkapan tersedia untuk beberapa file tes, kita harus mendefinisikan fungsi perlengkapan dalam sebuah file bernama conftest.py.conftest.py dijelaskan di bab selanjutnya.

Kita dapat menentukan fungsi fixture dalam file ini untuk membuatnya dapat diakses di beberapa file uji.

Buat file baru conftest.py dan tambahkan kode di bawah ini ke dalamnya -

import pytest

@pytest.fixture
def input_value():
   input = 39
   return input

Edit test_div_by_3_6.py untuk menghapus fungsi fixture -

import pytest

def test_divisible_by_3(input_value):
   assert input_value % 3 == 0

def test_divisible_by_6(input_value):
   assert input_value % 6 == 0

Buat file baru test_div_by_13.py -

import pytest

def test_divisible_by_13(input_value):
   assert input_value % 13 == 0

Sekarang, kami memiliki file test_div_by_3_6.py dan test_div_by_13.py memanfaatkan perlengkapan yang ditentukan dalam conftest.py.

Jalankan tes dengan menjalankan perintah berikut -

pytest -k divisible -v

Perintah di atas akan menghasilkan hasil sebagai berikut -

test_div_by_13.py::test_divisible_by_13 PASSED
test_div_by_3_6.py::test_divisible_by_3 PASSED
test_div_by_3_6.py::test_divisible_by_6 FAILED
============================================== FAILURES
==============================================
________________________________________ test_divisible_by_6
_________________________________________
input_value = 39
   def test_divisible_by_6(input_value):
>  assert input_value % 6 == 0
E  assert (39 % 6) == 0
test_div_by_3_6.py:7: AssertionError
========================== 1 failed, 2 passed, 6 deselected in 0.09 seconds
==========================

Tes akan mencari fixture dalam file yang sama. Karena fixture tidak ditemukan di file, itu akan memeriksa fixture di file conftest.py. Saat menemukannya, metode fixture dipanggil dan hasilnya dikembalikan ke argumen input tes.

Parameterisasi pengujian dilakukan untuk menjalankan pengujian terhadap beberapa set input. Kita bisa melakukan ini dengan menggunakan penanda berikut -

@pytest.mark.parametrize

Salin kode di bawah ini ke dalam file bernama test_multiplication.py -

import pytest

@pytest.mark.parametrize("num, output",[(1,11),(2,22),(3,35),(4,44)])
def test_multiplication_11(num, output):
   assert 11*num == output

Di sini pengujian mengalikan input dengan 11 dan membandingkan hasilnya dengan output yang diharapkan. Tes ini memiliki 4 set input, masing-masing memiliki 2 nilai - satu adalah angka yang akan dikalikan dengan 11 dan yang lainnya adalah hasil yang diharapkan.

Jalankan tes dengan menjalankan perintah berikut -

Pytest -k multiplication -v

Perintah di atas akan menghasilkan output berikut -

test_multiplication.py::test_multiplication_11[1-11] PASSED
test_multiplication.py::test_multiplication_11[2-22] PASSED
test_multiplication.py::test_multiplication_11[3-35] FAILED
test_multiplication.py::test_multiplication_11[4-44] PASSED
============================================== FAILURES
==============================================
_________________ test_multiplication_11[3-35] __________________
num = 3, output = 35
   @pytest.mark.parametrize("num, output",[(1,11),(2,22),(3,35),(4,44)])
   def test_multiplication_11(num, output):
>  assert 11*num == output
E  assert (11 * 3) == 35
test_multiplication.py:5: AssertionError
============================== 1 failed, 3 passed, 8 deselected in 0.08 seconds
==============================

Pada bab ini, kita akan belajar tentang tes Lewati dan Xfail di Pytest.

Sekarang, pertimbangkan situasi di bawah ini -

  • Tes tidak relevan untuk beberapa waktu karena beberapa alasan.
  • Sebuah fitur baru sedang diterapkan dan kami telah menambahkan pengujian untuk fitur tersebut.

Dalam situasi ini, kami memiliki opsi untuk membatalkan pengujian atau melewati pengujian.

Pytest akan menjalankan tes xfailed, tetapi tidak akan dianggap sebagai bagian yang gagal atau lulus tes. Rincian tes ini tidak akan dicetak meskipun tes gagal (ingat pytest biasanya mencetak detail tes yang gagal). Kita dapat melakukan xfail tes menggunakan penanda berikut -

@pytest.mark.xfail

Melewati tes berarti tes tidak akan dijalankan. Kita bisa melewatkan tes menggunakan penanda berikut -

@pytest.mark.skip

Nanti, jika tes menjadi relevan, kami dapat menghapus penanda.

Edit test_compare.py kita sudah harus menyertakan penanda xfail dan lewati -

import pytest
@pytest.mark.xfail
@pytest.mark.great
def test_greater():
   num = 100
   assert num > 100

@pytest.mark.xfail
@pytest.mark.great
def test_greater_equal():
   num = 100
   assert num >= 100

@pytest.mark.skip
@pytest.mark.others
def test_less():
   num = 100
   assert num < 200

Jalankan tes menggunakan perintah berikut -

pytest test_compare.py -v

Setelah dieksekusi, perintah di atas akan menghasilkan hasil sebagai berikut -

test_compare.py::test_greater xfail
test_compare.py::test_greater_equal XPASS
test_compare.py::test_less SKIPPED
============================ 1 skipped, 1 xfailed, 1 xpassed in 0.06 seconds
============================

Dalam skenario nyata, setelah versi baru kode siap untuk diterapkan, itu pertama kali diterapkan ke lingkungan pra-prod / pementasan. Kemudian rangkaian pengujian dijalankan di atasnya.

Kode memenuhi syarat untuk diterapkan ke produksi hanya jika rangkaian pengujian berhasil. Jika ada kegagalan pengujian, apakah satu atau banyak, kode tidak siap produksi.

Oleh karena itu, bagaimana jika kita ingin menghentikan eksekusi rangkaian pengujian segera setelah sejumlah n pengujian gagal. Ini bisa dilakukan di pytest menggunakan maxfail.

Sintaks untuk menghentikan eksekusi rangkaian pengujian segera setelah n jumlah pengujian gagal adalah sebagai berikut -

pytest --maxfail = <num>

Buat file test_failure.py dengan kode berikut.

import pytest
import math

def test_sqrt_failure():
   num = 25
   assert math.sqrt(num) == 6

def test_square_failure():
   num = 7
   assert 7*7 == 40

def test_equality_failure():
   assert 10 == 11

Semua 3 tes akan gagal saat menjalankan file tes ini. Di sini, kami akan menghentikan eksekusi pengujian setelah satu kegagalan itu sendiri dengan -

pytest test_failure.py -v --maxfail = 1
test_failure.py::test_sqrt_failure FAILED
=================================== FAILURES
=================================== _______________________________________
test_sqrt_failure __________________________________________
   def test_sqrt_failure():
   num = 25
>  assert math.sqrt(num) == 6
E  assert 5.0 == 6
E  + where 5.0 = <built-in function sqrt>(25)
E  + where <built-in function sqrt>= math.sqrt
test_failure.py:6: AssertionError
=============================== 1 failed in 0.04 seconds
===============================

Pada hasil di atas, kita bisa melihat eksekusi dihentikan pada satu kegagalan.

Secara default, pytest menjalankan pengujian secara berurutan. Dalam skenario nyata, rangkaian pengujian akan memiliki sejumlah file pengujian dan setiap file akan memiliki banyak pengujian. Ini akan menyebabkan waktu eksekusi yang besar. Untuk mengatasinya, pytest memberi kita opsi untuk menjalankan tes secara paralel.

Untuk ini, pertama kita perlu menginstal plugin pytest-xdist.

Instal pytest-xdist dengan menjalankan perintah berikut -

pip install pytest-xdist

Sekarang, kita bisa menjalankan tes dengan menggunakan sintaks pytest -n <num>

pytest -n 3

-n <num> menjalankan pengujian dengan menggunakan banyak pekerja, ini dia 3.

Kami tidak akan memiliki banyak perbedaan waktu ketika hanya ada beberapa tes yang harus dijalankan. Namun, itu penting jika paket pengujian besar.

Kami dapat membuat detail eksekusi uji dalam file xml. File xml ini berguna terutama jika kami memiliki dasbor yang memproyeksikan hasil tes. Dalam kasus seperti itu, xml dapat diurai untuk mendapatkan detail eksekusi.

Kami sekarang akan menjalankan tes dari test_multiplcation.py dan menghasilkan xml dengan menjalankan

pytest test_multiplication.py -v --junitxml="result.xml"

Sekarang kita bisa melihat result.xml dibuat dengan data berikut -

<?xml version = "1.0" encoding = "utf-8"?>
<testsuite errors = "0" failures = "1"
name = "pytest" skips = "0" tests = "4" time = "0.061">
   <testcase classname = "test_multiplication"          
      file = "test_multiplication.py"
      line = "2" name = "test_multiplication_11[1-11]"
      time = "0.00117516517639>
   </testcase>
   
   <testcase classname = "test_multiplication"    
      file = "test_multiplication.py"
      line = "2" name = "test_multiplication_11[2-22]"
      time = "0.00155973434448">
   </testcase>

   <testcase classname = "test_multiplication" 
      file = "test_multiplication.py"
      line = "2" name = "test_multiplication_11[3-35]" time = "0.00144290924072">
      failure message = "assert (11 * 3) == 35">num = 3, output = 35

         @pytest.mark.parametrize("num,
         output",[(1,11),(2,22),(3,35),(4,44)])
            
         def test_multiplication_11(num, output):> 
         assert 11*num == output
         E assert (11 * 3) == 35

         test_multiplication.py:5: AssertionErro
      </failure>
   </testcase>
   <testcase classname = "test_multiplication" 
      file = "test_multiplication.py"
      line = "2" name = "test_multiplication_11[4-44]"
      time = "0.000945091247559">
   </testcase>
</testsuite>

Ini, tagnya <testsuit> merangkum ada 4 tes dan jumlah kegagalan adalah 1.

  • Tag <testcase> memberikan detail dari setiap pengujian yang dijalankan.

  • Tag <failure> memberikan detail kode pengujian yang gagal.

Dalam tutorial pytest ini, kami membahas area berikut -

  • Menginstal pytest ..
  • Mengidentifikasi file uji dan fungsi uji.
  • Menjalankan semua file tes menggunakan pytest –v.
  • Menjalankan file tertentu menggunakan pytest <filename> -v.
  • Jalankan pengujian dengan mencocokkan substring pytest -k <substring> -v.
  • Jalankan tes berdasarkan penanda pytest -m <marker_name> -v.
  • Membuat perlengkapan menggunakan @ pytest.fixture.
  • conftest.py memungkinkan mengakses perlengkapan dari banyak file.
  • Tes parametrizing menggunakan @ pytest.mark.parametrize.
  • Pengujian yang gagal menggunakan @ pytest.mark.xfail.
  • Melewati pengujian menggunakan @ pytest.mark.skip.
  • Hentikan eksekusi uji pada n kegagalan menggunakan pytest --maxfail = <num>.
  • Menjalankan pengujian secara paralel menggunakan pytest -n <num>.
  • Menghasilkan hasil xml menggunakan pytest -v --junitxml = "result.xml".

Tutorial ini memperkenalkan Anda ke framework pytest. Sekarang Anda bisa mulai menulis tes menggunakan pytest.

Sebagai praktik yang baik -

  • Buat file pengujian yang berbeda berdasarkan fungsionalitas / modul yang diuji.
  • Berikan nama yang bermakna untuk menguji file dan metode.
  • Miliki cukup penanda untuk mengelompokkan tes berdasarkan berbagai kriteria.
  • Gunakan perlengkapan kapan pun dibutuhkan.