0% menganggap dokumen ini bermanfaat (0 suara)
102 tayangan159 halaman

07 - Python - Unit Testing

Modul ini membahas tentang unit testing untuk data science menggunakan bahasa pemrograman Python. Topik utama yang dibahas adalah konsep dasar unit testing, membuat unit test sederhana menggunakan library pytest, dan penggunaan assert untuk menguji keluaran fungsi."

Diunggah oleh

bayu zen
Hak Cipta
© © All Rights Reserved
Kami menangani hak cipta konten dengan serius. Jika Anda merasa konten ini milik Anda, ajukan klaim di sini.
Format Tersedia
Unduh sebagai PPTX, PDF, TXT atau baca online di Scribd
0% menganggap dokumen ini bermanfaat (0 suara)
102 tayangan159 halaman

07 - Python - Unit Testing

Modul ini membahas tentang unit testing untuk data science menggunakan bahasa pemrograman Python. Topik utama yang dibahas adalah konsep dasar unit testing, membuat unit test sederhana menggunakan library pytest, dan penggunaan assert untuk menguji keluaran fungsi."

Diunggah oleh

bayu zen
Hak Cipta
© © All Rights Reserved
Kami menangani hak cipta konten dengan serius. Jika Anda merasa konten ini milik Anda, ajukan klaim di sini.
Format Tersedia
Unduh sebagai PPTX, PDF, TXT atau baca online di Scribd
Anda di halaman 1/ 159

Python – Unit

Testing

Last Update: 16-09-2022


Python – Membersihkan Data menggunakan Pyspark

Pathway Data Science

Sub-pathway Artificial Intelligence

Kompetensi T-25 Computational Modelling

Key Element T-25_C_02 Iteration

Level Intermediate

Developer Berinovasi.id
Version 01

Approval 1. Manager Learning Development Digital Services


2. SM School of Dig. Platform & Dig. Services

Python – Unit Testing


1
Modul Pembelajaran : Unit testing for Data Science in Python

Modul ini membahas tentang testing untuk data science


Deskripsi Modul :

Peserta mengetahui penjelasan unit testing, assert, data set, mocking


Enabling Learning Objective :

Daftar Topik :

1. Unit testing
Keywords :
2. Assert
3. data set
4. mocking

Python – Unit Testing


2
Topik 1 – Unit
Testing

Python – Unit Testing


3
1. Unit Testing
Konsep Dasar

Apabila kita mengimplementasikan suatu fungsi python, bagaimana cara kita mengetahui bahwa fungsi
sudah diimplementasikan dengan benar? Cara termudahnya adalah dengan menguji fungsi tersebut pada
beberapa argumen, dan memastikan bahwa output yang dikeluarkan sudah benar. Namun, cara ini akan
menjadi sangat tidak efisien apabila funsgi digunakan untuk project data science yang lebih kompleks.

def my_function(argument): my_function(argument_2) my_function(argument_3)

my_function(argument_1)

...

return_value_1 return_value_2 return_value_3

Python – Unit Testing > Unit Testing > Konsep Dasar


4
1. Unit Testing
Konsep Dasar

Berikut merupakan siklus suatu fungsi. Pertama, kita


implementasikan fungsi tersebut kemudian dilakukan
testing.
Apabila lulus testing, maka implementasi diterima.
Namun apabila tidak lulus, maka dilakukan perbaikan
pada bugs dan dicoba test kembali.
Di lain waktu, sangat mungkin akan dibutuhkan fitur baru
atau diminta untuk melakukan perbaikan function,
sehingga dilakukan implementasi dan test kembali.
Di kemungkinan lain, bisa jadi ditemukan bugs baru yang
sebelumnya tidak terlihat, sehingga dilakukan perbaikan
bugs dan test kembali
Setiap kali dilakukan modifikasi pada function, maka
harus selalu dilakukan test.

Python – Unit Testing > Unit Testing > Konsep Dasar


5
1. Unit Testing
Konsep Dasar
Perhatikan contoh function row_to_list dengan argumen berupa
def row_to_list(row):
string. Contoh argumen yang digunakan yaitu single row data
housing_data.txt. ...

Data input menyimpan informasi luas rumah, kemudian tab, lalu harga
rumah tersebut dan ditutup dengan new line character.

Argument Type Return value

"2,081\t314,942\n" Valid ["2,081",


"314,942"]

Apabila function row_to_list dijalankan dengan input housing_data.txt,


maka akan menampilkan hasil berupa list 2 elemen yang terdiri dari luas File: housing_data.txt

rumah dan harga rumah.


Python – Unit Testing > Unit Testing > Konsep Dasar
6
1. Unit Testing
Konsep Dasar
Sayangnya, data file masih belum bersih dan beberapa baris data tidak
def row_to_list(row):
memiliki format yang sesuai. Contohnya, baris ketiga tidak memiliki
informasi luas dan baris ke-sepuluh tidak memiliki tab. Untuk baris invalid ...

tersebut, maka dihasilkan None dari function.

Argument Type Return value

"2,081\t314,942\n" Valid ["2,081",


"314,942"]

"\t293,410\n" Invalid None

"1,463238,765\n" Invalid None

File: housing_data.txt

Python – Unit Testing > Unit Testing > Konsep Dasar


7
1. Unit Testing
Konsep Dasar
row_to_list("2,081\t314,942\n")
Untuk menguji function row_to_list tersebut, perlu dicek seluruh
arguments satu-persatu untuk melihat validitasnya. ["2,081", "314,942"]

Mengingat pada suatu cycle function umumnya perlu diuji sebanyak 100 row_to_list("\t293,410\n")
kali, dan mengasumsikan setiap pengujian memerlukan waktu 5 menit,
None
None
maka diperlukan sekitar 8 jam untuk menguji function secara manual.
row_to_list("1,463238,765\n")
Menggunakan unit testing mampu mengurangi durasi pengujian
menjadi 1 jam saja. Dalam skala project data science yang besar, tentu None
None
unit testing menjadi suatu hal yang wajib dikuasai oleh data scientist.
...

Python – Unit Testing > Unit Testing > Konsep Dasar


8
1. Unit Testing
Konsep Dasar
Kali ini akan dipelajari bagaimana membuat unit tests. Telah disediakan
contoh project data science yang memprediksi harga perumahan
menggunakan linear regression seperti pada grafik di samping.

Code yang digunakan, beserta implementasi function seperti row_to_list(), tersedia di


GitHub Repository

Python – Unit Testing > Unit Testing > Konsep Dasar


9
1. Unit Testing
Membuat Unit Test Sederhana Menggunakan pytest
Terdapat banyak python library untuk menulis unit testing, seperti pytest, unittest, nosetests, dan
doctest.

Pada kali ini akan digunakan pytest, dengan kelebihannya yaitu:


● Memiliki seluruh fitur yang sering digunakan
● Paling mudah untuk digunakan
● Paling populer

Python – Unit Testing > Unit Testing > Membuat Unit Test Sederhana Menggunakan Pytest
10
1. Unit Testing
Membuat Unit Test Sederhana Menggunakan pytest
1. Buat File Test Module
Untuk memulai unit testing menggunakan pytest, pertama-tama perlu dibuat file
test_row_to_list.py. Apabila pytest melihat file dengan awalan ‘test_’, maka
akan segera dikenali sebagai file python spesial yang di dalamnya terdapat unit tests.
Sehingga, format penamaan dengan awalan ‘test_’ harus diikuti. File yang
mengandung unit tests disebut sebagai test module.

2. Import
import pytest Dalam file test module, perlu terlebih dahulu import pytest, kemudian
import row_to_list import function yang akan ditest row_to_list.

Python – Unit Testing > Unit Testing > Membuat Unit Test Sederhana Menggunakan Pytest
11
1. Unit Testing
Membuat Unit Test Sederhana Menggunakan pytest
3. Unit Test merupakan Function Python
Unit test dituliskan sebagai function python yang namanya
diawali dengan ‘test_’, sama seperti module test. Dengan import pytest
begitu, pytest dapat mengenali function tersebut sebagai unit import row_to_list
test, bukan function biasa.

Unit test biasanya berkoresponden terhadap tepat satu entry def test_for_clean_row():
argument dan menghasilkan tabel return value untuk function
row_to_list().
Argument Type Return value
Unit test mengecek apakah row_to_list() memiliki return
value yang sesuai apabila dijalankan pada argumen tertentu. "2,081\t314,942\n" Valid ["2,081",
"314,942"]
Kali ini digunakan argument clean row, sehingga unit test yang
dibuat akan diberi nama test_for_clean_row().

Python – Unit Testing > Unit Testing > Membuat Unit Test Sederhana Menggunakan Pytest
12
1. Unit Testing
Membuat Unit Test Sederhana Menggunakan pytest

import pytest 4. Assertion


import row_to_list
Statement assert memerlukan argument berupa boolean expression.
Apabila bernilai True, maka assert statement akan dilewati dan
def test_for_clean_row(): memberikan output kosong. Apabila bernilai False, maka akan
assert ... memunculkan AssertionError.

assert boolean_expression assert False

assert True Traceback (most recent call last):


File "<stdin>", line 1, in
<module>

AssertionError

Python – Unit Testing > Unit Testing > Membuat Unit Test Sederhana Menggunakan Pytest
13
1. Unit Testing
Membuat Unit Test Sederhana Menggunakan pytest

4. Assertion (lanjutan)
import pytest import row_to_list
Dilakukan cek apakah row_to_list
menghasilkan list yang sesuai pada data clean
row. def test_for_clean_row():
Function row_to_list digunakan pada assert row_to_list("2,081\t314,942\n") == \
argumen == list yang dianggap sesuai. Apabila
function berhasil, maka akan melewati assert ["2,081", "314,942"]
statement dan memberikan output blank. Argument Type Return value

Apabila function memiliki bug, maka akan "2,081\t314,942\n" Valid ["2,081",


memberikan hasil False dan assert akan "314,942"]
menampilkan AssertionError, dan test dikatakan
gagal.

Python – Unit Testing > Unit Testing > Membuat Unit Test Sederhana Menggunakan Pytest
14
1. Unit Testing
Membuat Unit Test Sederhana Menggunakan pytest

4. Assertion (lanjutan) import pytest import row_to_list


Untuk baris kedua, dibuat unit test
def test_for_clean_row():
test_on_missing_area() karena
assert row_to_list("2,081\t314,942\n") == \
argumen tidak memiliki informasi area. Maka
["2,081", "314,942"]
ditentukan bahwa outputnya merupakan
None. def test_for_missing_area():
assert row_to_list("\t293,410\n") is None
Saat melakukan pengecekan pada null value,
penulisan berupa boolean var is None, Argument Type Return value
bukan var == None
"2,081\t314,942\ Valid ["2,081",
n"
"314,942"]

"\t293,410\n" Invalid None

Python – Unit Testing > Unit Testing > Membuat Unit Test Sederhana Menggunakan Pytest
15
1. Unit Testing
Membuat Unit Test Sederhana Menggunakan pytest
import pytest import row_to_list
4. Assertion (lanjutan)
Untuk baris ketiga, dibuat unit test
test_for_missing_tab() karena def test_for_clean_row():
assert row_to_list("2,081\t314,942\n")
argumen tidak memiliki tab yang == \ ["2,081", "314,942"]
memisahkan luas dan harga bangunan.
Assert statement yang digunakan mirip
def test_for_missing_area():
seperti contoh pada baris kedua.
assert row_to_list("\t293,410\n") is
Argument Type Return value
None
"2,081\t314,942\ Valid ["2,081",
n"
"314,942"] def test_for_missing_tab():
"\t293,410\n" Invalid None assert row_to_list("1,463238,765\n") is
Invalid None
"1,463238,765\n" None

Python – Unit Testing > Unit Testing > Membuat Unit Test Sederhana Menggunakan Pytest
16
1. Unit Testing
Membuat Unit Test Sederhana Menggunakan pytest

pytest test_row_to_list.py 5. Jalankan unit test


Untuk memastikan row_to_list() berfungsi dengan baik
kapanpun, maka dilakukan run test module.
Umumnya run test dilakukan dengan membuka command
line kemudian tuilis pytest diikuti nama test module.

Pada contoh, dituliskan tests dalam test module.


Dalam IPythonnya, command line expression
digunakan dengan menambahkan tanda seru ( ! )
sebelum expression. Hasilnya berupa test report,
yang berisi informasi bugs (bila ada).

Python – Unit Testing > Unit Testing > Membuat Unit Test Sederhana Menggunakan Pytest
17
1. Unit Testing
Memahami Test Result Report
Ketika pengguna menjalankan tests, akan dihasilkan test result report yang menyimpan informasi untuk
menyelesaikan bugs.
Argument Type Return value

"2,081\t314,942\n" Valid ["2,081",


import pytest
"314,942"]
import row_to_lisy
def test_for_clean_row(): "\t293,410\n" Invalid None
assert row_to_list("2,081\t314,942\n")
"1,463238,765\n" Invalid None
== \ ["2,081", "314,942"]

def test_for_missing_area():
Sebagai contoh, digunakan test module
assert row_to_list("\t293,410\n") is None test_row_to_list.py yang mengandung 3 unit
tests untuk fungsi row_to_list(). Unit test akan
def test_for_missing_tab(): menguji apakah row_to_list() menghasilkan nilai
assert row_to_list("1,463238,765\n") is None yang sesuai untuk clean row, row dengan missing area,
dan row dengan missing tab secara berurutan.
Python – Unit Testing > Unit Testing > Memahami Test Result Report
18
1. Unit Testing
Memahami Test Result Report
Menjalankan tests di IPython console menghasilkan output yang sangat banyak. Output ini disebut test
result report.
!pytest test_row_to_list.py Unit test dijalankan dengan
memanggil !pytest
kemudian tuliskan tests
module yang telah dibuat.

Berikut merupakan cuplikan


dari test result report.

Bagian-bagian dari test result


akan dibahas lebih detail di
slide selanjutnya.

Python – Unit Testing > Unit Testing > Membuat Unit Test Sederhana Menggunakan Pytest
19
1. Unit Testing
Memahami Test Result Report
Bagian 1: General Information

============================= test session starts ==============================


platform linux -- Python 3.6.7, pytest-4.0.1, py-1.8.0, pluggy-0.9.0 rootdir:
/tmp/tmpvdblq9g7, inifile:

plugins: mock-1.10.0

Bagian ini menampilkan informasi terkait operating system, Pyton version, pytest package versions,
directory yang digunakan, dan plugin pytest.
Bagian ini memuat informasi umum sehingga tidak banyak yang bisa dilakukan pada bagian pertama.

Python – Unit Testing > Unit Testing > Membuat Unit Test Sederhana Menggunakan Pytest
20
1. Unit Testing
Memahami Test Result Report
Bagian 2: Test Result

Pada output tertulis ‘collected 3 items’, yang artinya ditemukan 3 unit tests yang akan dijalankan. Baris
selanjutnya memuat nama test module, yaitu test_row_to_list.py, diikuti dengan .F. yang
merepresentasikan hasil unit test.
Character Meaning When Action F berarti Failure. Unit test dikatakan
An exception is Fix the function gagal apabila muncul exception saat
F Failure raised when or unit test.
running unit test. menjalankan unit test.

Python – Unit Testing > Unit Testing > Membuat Unit Test Sederhana Menggunakan Pytest
21
1. Unit Testing
Memahami Test Result Report
Bagian 2: Test Result

Hal ini biasanya disebabkan ketika assert statement


def test_for_missing_area():
memunculkan AssertionError. Artinya, function memiliki bug
assert row_to_list("\t293,410") dan harus segera diperbaiki.
is None

Penyebab lainnya yaitu beberapa exception muncul ketika


def test_for_missing_area():
sedang menjalankan unit test. Contoh penulisan none akan
assert row_to_list("\t293,410") mengakibatkan NameError. Karena hal tersebut bukan dari
is none assert statement, sehingga pengguna tidak bisa
menyimpulkan apapun dari hasil unit test. Dalam hal ini,
maka unit test perlu diperbaiki sehingga bisa menjalankan
assert statement.

Python – Unit Testing > Unit Testing > Membuat Unit Test Sederhana Menggunakan Pytest
22
1. Unit Testing
Memahami Test Result Report
Bagian 2: Test Result
Character Meaning When Action

An exception is raised when running unit test. Fix the function or unit
F Failure test.

No exception raised when running unit Everything is fine.


. Passed test Be happy!

Titik (.) berarti unit test berhasil dilewati dan tidak ada exception yang ditimbulkan oleh assert statement
maupun penyebab lainnya selama unit test berlangsung.

Untuk test module test_row_to_list.py yang memiliki hasil .F. berarti test pertama berhasil, kedua
gagal, dan ketiga berhasil.

Python – Unit Testing > Unit Testing > Membuat Unit Test Sederhana Menggunakan Pytest
23
1. Unit Testing
Memahami Test Result Report
Bagian 3: Informasi Failed Test

Bagian selanjutnya memuat informasi terkait test yang gagal (failed). Dapat dilihat bahwa unit test
test_on_missing_area() gagal. Baris kode yang menimbulkan exception ditandai dengan simbol > di sebelah
kiri. Pada kali ini, exception disebabkan oleh assert statement.

> assert row_to_list("\t293,410\n") is None

Python – Unit Testing > Unit Testing > Membuat Unit Test Sederhana Menggunakan Pytest
24
1. Unit Testing
Memahami Test Result Report
Bagian 3: Informasi Failed Test

Baris dengan where menampilkan segala nilai yang dihasilkan saat assert statement dijalankan. Pada
contoh, nilai yang dihasilkan dari row_to_list() adalah list yang berisi string kosong dan string
‘293,410’, sementara nilai yang diekspektasikan adalah None. Perbedaan ini akan menjadi titik awal untuk
dilakukan debugging, dengan mencari tahu kenapa row_to_list() menghasilkan list dan bukan
None.

Python – Unit Testing > Unit Testing > Membuat Unit Test Sederhana Menggunakan Pytest
25
1. Unit Testing
Memahami Test Result Report
Bagian 4: Test Result

====================== 1 failed, 2 passed in 0.03 seconds ======================

Baris terakhir menampilkan ringkasan test result, yang menyatakan ‘1 test gagal, dan 2 berhasil’. Terdapat
pula informasi tambahan mengenai durasi test berjalan, yaitu 0.03 detik. Tentu saja, hal ini jauh lebih cepat
dibandingkan test secara manual.

Python – Unit Testing > Unit Testing > Membuat Unit Test Sederhana Menggunakan Pytest
26
1. Unit Testing
Jenis dan Keuntungan Test Lainnya
Sebelumnya, telah dipelajari bahwa unit test dapat menyimpan banyak waktu dibandingkan manual test. Namun,
masih banyak keuntungan dari unit testing, salah satunya unit testing bisa berperan sebagai dokumentasi.
import pytest Unit test yang telah dibuat bisa berperan sebagai
import row_to_list dokumentasi yang menginformasikan pengguna
lainnya terkait tujuan function.
def test_for_clean_row():
Argument Type Return value
assert row_to_list("2,081\t314,942\n") == \
["2,081", "314,942"] "2,081\t314,942\ Valid ["2,081",
n"
"314,942"]
def test_for_missing_area():
"\t293,410\n" Invalid None
assert row_to_list("\t293,410\n") is None
"1,463238,765\n" Invalid None
def test_for_missing_tab():
Pengguna dapat membuat ulang tabel return value
assert row_to_list("1,463238,765\n") is None hanya dengan mencocokan boolean expressions
pada assert statement.
Python – Unit Testing > Unit Testing > Jenis dan Keuntungan Test Lainnya
27
1. Unit Testing
Jenis dan Keuntungan Test Lainnya
Pada suatu kondisi, pengguna seringkali diminta untuk menebak kegunaan function dengan melihat test
modulenya. Hal ini bisa diselesaikan dengan menjalankan !cat diikuti dengan nama test module di IPython
console untuk melihat unit testsnya.

!cat test_row_to_list.py Contoh di samping menampilkan contoh unit test pada


test_row_to_list.py yang ditampilkan
menggunakan !cat test_row_to_list.py

Unit test meningkatkan kepercayaan pengguna pada


suatu package karena pengguna bisa menjalankan unit
tests tersebut dan memverifikasi kegunaanfunction
tersebut.

Python – Unit Testing > Unit Testing > Jenis dan Keuntungan Test Lainnya
28
1. Unit Testing
Jenis dan Keuntungan Test Lainnya
Berikut merupakan contoh package NumPy dalam github page-nya. NumPy mendapatkan kepercayaan lebih dari pengguna karena
badges-badges yang ditampilkan.

Menunjukkan berapa
banyak bagian code
base yang dilakukan
unit testing

Menunjukkan
apakah function
berhasil melewati
unit testing atau
tidak

Python – Unit Testing > Unit Testing > Jenis dan Keuntungan Test Lainnya
29
1. Unit Testing
Jenis dan Keuntungan Test Lainnya
Unit test juga mampu mengurangi downtime pada sistem produktif.

Jika pengguna /developer membuat kesalahan


dan menjalankan function bermasalah ke
dalam sistem produktif, maka sistem akan
terganggu dan tentunya menghambat
pekerjaan.

Python – Unit Testing > Unit Testing > Jenis dan Keuntungan Test Lainnya
30
1. Unit Testing
Jenis dan Keuntungan Test Lainnya
Hambatan akibat bad code yang diimplementasikan ke dalam sistem dapat dicegah dengan memanfaatkan continuous learning
atau CI.

● CI menjalankan semua unit tests pada code yang digunakan


● Apabila terdapat unit test yang gagal, maka CI akan menolak
code tersebut untuk dilanjutkan ke sistem
● Tidak terjadi downtime
● CI juga akan menginformasikan pengguna bahwa code perlu
diperbaiki.

Apabila kita menjalankan sistem produktif yang dibutuhkan


oleh pengguna lainnya, kita harus menulis unit test dan
menggunakan CI.

Python – Unit Testing > Unit Testing > Jenis dan Keuntungan Test Lainnya
31
1. Unit Testing
Jenis dan Keuntungan Test Lainnya
Maka, dapat disimpulkan bahwa keuntungan unit test di antaranya yaitu:
● Memerlukan waktu yang lebih sedikit
● Meningkatkan dokumentasi kode
● Meningkatkan kepercayaan pengguna
● Mengurangi downtime

Python – Unit Testing > Unit Testing > Jenis dan Keuntungan Test Lainnya
32
1. Unit Testing
Jenis dan Keuntungan Test Lainnya

2. Feature Module yang


terdiri dari functions
yang digunakan
untuk menghitung
1. Data Module yang fitur dari clean data
memproduksi clean data dari
raw data pada data ‘Housing 3. Model Module yang
Area and Price’. Data module menghasilkan model
terdiri dari functions prediksi harga rumah
row_to_list() dan dari fitur yang telah
convert_to_int(). dihitung

Python – Unit Testing > Unit Testing > Jenis dan Keuntungan Test Lainnya
33
1. Unit Testing
Jenis dan Keuntungan Test Lainnya

Test yang akan dibuat disebut unit test


karena digunakan untuk menguji sebuah
unit, seperti row_to_list()

Unit adalah bagian kecil dan independent


dari code. Unit bisa berupa function
maupun class.

Python – Unit Testing > Unit Testing > Jenis dan Keuntungan Test Lainnya
34
1. Unit Testing
Jenis dan Keuntungan Test Lainnya Integration test

Integration test menguji integrasi antar


beberapa unit yang saling terhubung.
Contohnya, apakah module data dan fitur
mampu bekerja sama dengan baik. Dalam
contoh ini, maka argumennya adalah raw
data dan return values yang diuji adalah
features.

Python – Unit Testing > Unit Testing > Jenis dan Keuntungan Test Lainnya
35
1. Unit Testing
Jenis dan Keuntungan Test Lainnya End to end test

End to end test menguji keseluruhan


software sekaligus. Test dijalankan dari
awal yaitu raw data, melalui seluruh unit
hingga akhir, dan memastikan apakah
model yang dihasilkan sudah sesuai.

Python – Unit Testing > Unit Testing > Jenis dan Keuntungan Test Lainnya
36
Topik 2 – Assert
Statement

Python – Unit Testing > Assert Statement


37
2. Assert Statement
Pengantar
Selain boolean expression, pengguna bisa menambahkan argumen kedua pada assert statement, yakni message.

assert boolean_expression, message Message hanya akan ditampilkan apabila assert


statement menimbulkan AssertionError. Message
assert 1 == 2, "One is not equal to two!" harus menjelaskan penyebab timbulnya
AssertionError tersebut.
raceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError: One is not equal to two!

assert 1 == 1, "This will not be printed


since assertion passes"
Apabila assert statement berhasil dilewati, maka
tidak akan ditampilkan message apapun.

Python – Unit Testing > Assert Statement > Pengantar


38
2. Assert Statement
Pengantar
Berikut merupakan cara meningkatkan kualitas test dengan menambahkan argumen message.

import pytest Pertama, simpan return value dari row_to_list


... ke dalam variabel actual.
def test_for_missing_area_with_message(): Kemudian definisikan variabel baru expected
actual = row_to_list("\t293,410\n") yang menyimpan return value yang diekspektasikan.
expected = None Lalu, definisikan message yang menyimpan
message = ("row_to_list('\t293,410\ argumen, return value aktual dan ekspektasi -
n') " "returned {0} instead apapun yang perlu diperbaiki apabila test gagal.
" "of {1}".format(actual,
Ditutup dengan assert statement beserta argument
expected) )
message.
assert actual is expected, message

Python – Unit Testing > Assert Statement > Pengantar


39
2. Assert Statement
Pengantar
test_on_missing_area() Contoh unit test tanpa message, ditampilkan
default output milik pytest untuk tests yang
E AssertionError: assert ['', '293,410'] gagal.
is None
E + where ['', '293,410'] =
row_to_list('\t293,410\n') Pada unit test dengan message, output default
digantikan dengan pesan yang mudah dimengerti
pada AssertionError.
test_on_missing_area_with_message(

> assert actual is expected, message Disarankan untuk menuliskan message pada
E AssertionError: row_to_list('\t293,410\n') assertion statement karena lebih mudah
returned ['', '293,410'] dipahami. Selain itu, disarankan juga untuk
instead of None
menuliskan nilai dari variabel apapun yang dapat
assert ['', '293,410'] is None memudahkan debugging.

Python – Unit Testing > Assert Statement > Pengantar


40
2. Assert Statement
Pengantar
Python memiliki kondisi khusus dalam pengoperasioan float. Seperti pada contoh:

0.1 + 0.1 + 0.1 == 0.3 0.1 + 0.1 + 0.1 Python merepresentasikan float
sedikit berbeda seperti pada
False 0.30000000000000004 contoh di samping. Hal ini dapat
menggagalkan test.

assert 0.1 + 0.1 + 0.1 == 0.3, "Usual way to compare does not always
work with floats!"

Traceback (most recent call last):


File “<stdin>”. line 1, in <module>
AssertionError: Usual way to compare does not always work with floats!

Python – Unit Testing > Assert Statement > Pengantar


41
2. Assert Statement
Pengantar
Sehingga, dalam implementasinya, gunakan pytest.approx() untuk menguji float dengan benar.

assert 0.1 + 0.1 + 0.1 == pytest.approx(0.3)

pytest.approx() juga bisa digunakan pada NumPy arrays yang mengandung float.

assert np.array([0.1 + 0.1, 0.1 + 0.1 + 0.1]) == pytest.approx(np.array([0.2, 0.3]))

Python – Unit Testing > Assert Statement > Pengantar


42
2. Assert Statement
Pengantar
Unit test bisa memiliki lebih dari satu assert statement.

import pytest Pertama, dilakukan test apakah function


... menghasilkan integer apapun menggunakan
def test_on_string_with_one_comma(): fungsi isinstance(), dengan
return_value = convert_to_int("2,081")
return_value sebagai argumen pertama
dan int sebagai argumen kedua.
assert isinstance(return_value, int)
assert return_value == 2081 Kemudian dilanjutkan dengan assert
statement yang menguji apakah return value
sesuai dengan expected value.

Test akan berhasil apabila kedua assert statement berhasil, dan akan menampilkan
AssertionError apabila salah satu assert statement ada yang gagal.

Python – Unit Testing > Assert Statement > Pengantar


43
2. Assert Statement
Testing untuk Exception
Sejauh ini, telah dipelajari kegunaan assert statement untuk menguji apakah function menghasilkan output
sesuai ekspektasi atau tidak. Namun, seringkali function tidak menghasilkan apapun melainkan
menimbulkan exception.
import numpy as np
split_into_training_and_te
example_argument = np.array([[2081,314942],#must be two
sting_sets() mengharuskan array
[1059, 186606],dimentional memiliki kolom dan baris. Jika tidak,
[1148, 206186], maka tidak bisa dilakukan split.
]
split_into_training_and_testing_sets(example_argument)

Python – Unit Testing > Assert Statement > Testing untuk Exception
44
2. Assert Statement
Testing untuk Exception
Apabila fungsi dijalankan pada array satu dimensi, maka tidak akan menghasilkan apapun, melainkan
menimbulkan ValueError, yaitu salah satu jenis exception.
import numpy as np

example_argument = np.array([2081, 314942, 1059, 186606, 1148, 206186]) # one

dimensional

split_into_training_and_testing_sets(example_argument)

ValueError: Argument data array must be two dimensional. Got 1 dimensional array
instead!

Python – Unit Testing > Assert Statement > Testing untuk Exception
45
2. Assert Statement
Testing untuk Exception
Berikut merupakan contoh test untuk menguji apakah split_into_training_and_testing_set()
akan menimbulkan ValueError apabila diberikan array satu dimensi sebagai argumennya.

def test_valueerror_on_one_dimensional_argument():
example_argument = np.array([2081, 314942, 1059, 186606, 1148, 206186])
with pytest.raises(ValueError):

Python – Unit Testing > Assert Statement > Testing untuk Exception
46
2. Assert Statement
Testing untuk Exception
Struktur utama yang perlu diketahui adalah with statement.
with context_manager: with memiliki satu argument yang
disebut context manager.
print("This is part of the Seluruh code yang ada di dalam with
context") # any code
statement disebut sebagai context.
inside is the context

Context manager with context_manager:


menjalankan code # <--- Runs code on entering context
sebelum dan sesudah print("This is part of the context") # any code inside
memasuki context is
the context
# <--- Runs code on exiting context
Python – Unit Testing > Assert Statement > Testing untuk Exception
47
2. Assert Statement
Testing untuk Exception
Ditampilkan contoh context manager pytest.raises().
with pytest.raises(ValueError):
# <--- Does nothing on entering the context
print("This is part of the context")

# <--- If context raised ValueError, silence it.

# <--- If the context did not raise ValueError, raise an exception.

pytest.raises() memiliki satu argument ValueError. Context manager ini tidak menjalankan
code saat memasuki context, hanya saat meninggalkan context saja.
Apabila code dalam context menimbulkan ValueError, maka context manager akan membisukan error
tersebut. Namun apabila code tidak menimbulkan ValueError, maka context manager akan
menimbulkan exception.

Python – Unit Testing > Assert Statement > Testing untuk Exception
48
2. Assert Statement
Testing untuk Exception
Di bawah merupakan contoh di mana ValueError ditimbulkan dalam context namun dibisukan oleh context
manager., sehingga tidak menampilkan apapun pada output.
with pytest.raises(ValueError):
raise ValueError # context exits with ValueError
# <--- pytest.raises(ValueError) silences it

Di bawah ini merupakan contoh di mana tidak ada ValueError yang ditimbulkan di dalam context sehingga
context manager menimbulkan exception yang disebut Failed.
with pytest.raises(ValueError):
pass # context exits without raising a ValueError
# <--- pytest.raises(ValueError) raises Failed

Failed: DID NOT RAISE <class 'ValueError'>

Python – Unit Testing > Assert Statement > Testing untuk Exception
49
2. Assert Statement
Testing untuk Exception
def test_valueerror_on_one_dimensional_argument():
example_argument = np.array([2081, 314942, 1059, 186606, 1148, 206186]) with
pytest.raises(ValueError):

split_into_training_and_testing_sets(example_argument)

ValueError: Argument data array must be two dimensional. Got 1 dimensional array
instead!

Kembali pada unit testnya, kita gunakan function pada array satu dimensi di dalam context. Apabila
function menimbulkan ValueError seperti yang diekspektasikan, maka test berhasil. Apabila function
tidak menimbulkan ValueError, maka context manager akan menimbulkan Failed exception,
sehingga test dikatakan gagal.

Python – Unit Testing > Assert Statement > Testing untuk Exception
50
2. Assert Statement
Testing untuk Exception
ValueError: Argument data array must be two dimensional. Got 1 dimensional array
instead!

Kita juga dapat melakukan unit test terhadap detail dari exception. Contohnya, kita dapat menguji apakah
ValueError yang ditimbulkan sudah mengandung message yang sesuai atau belum. Dalam hal ini, message
yang sesuai diawali dengan “Argument data array must be two dimensional”.

Python – Unit Testing > Assert Statement > Testing untuk Exception
51
2. Assert Statement
Testing untuk Exception
def test_valueerror_on_one_dimensional_argument():
example_argument = np.array([2081, 314942, 1059, 186606, 1148, 206186])
with pytest.raises(ValueError) as exception_info: # store the exception
split_into_training_and_testing_sets(example_argument) # Check if
ValueError contains correct message
assert exception_info.match("Argument data array must be two dimensional. "
"Got 1 dimensional array instead!")
● Untuk menguji message, tambahkan as ke dalam with statement.
● exception_info menyimpan informasi tentang ValueError yang ditimbulkan dalam context.
● Setelah context selesai dijalankan, dilakukan uji apakah exception_info sudah memiliki message
yang sesuai dengan menggunakan assert statement disertai match() method. match() method
menggunakan string sebagai argumen dan menguji apakah string tersebut ada di dalam error message
atau tidak.
Python – Unit Testing > Assert Statement > Testing untuk Exception
52
2. Assert Statement
Well-tested Function
Perhatikan kembali function split_into_training_and_testing_sets(). Function tersebut
menggunakan array dua dimensi sebagai argumennya, kemudian memisahkan 75% barisnya secara acak
ke dalam training array, dan sisanya ke dalam training array. Function tersebut menghasilkan training dan
testing array sebagai dua tuple.
import numpy as np Karena function ini memiliki
example_argument = np.array([[2081,314942],#must be two karakteristik acak, maka yang diuji
[1059, 186606],dimentional adalah panjang dari training dan
[1148, 206186], testing array, bukan nilai yang ada di
dalamnya.
]
split_into_training_and_testing_sets(example_argument) Panjang dari training array menjadi
0.75 kali dari jumlah baris argumen.

Python – Unit Testing > Assert Statement > Well-tested Function


53
2. Assert Statement
Well-tested Function
Apabila array memiliki 8 baris, maka training Number of rows
(argument)
Number of rows (training
array)
Number of rows (testing array)

arraynya terdiri dari 6 baris dan testing-nya 2


baris, dan seterusnya. 8 int(0.75 * 8) = 6 8 - int(0.75 * 8) = 2

Umumnya, semakin banyak argumen yang dicek, 10 int(0.75 * 10) = 7 10 - int(0.75 * 10) = 3

maka semakin tinggi kepercayaan terhadap


23 int(0.75 * 23) = 17 23 - int(0.75 * 23) = 6
function tersebut.
... ... ...
Namun, karena tidak mungkin dibuat tests untuk
ribuan argumen, maka berapakah jumlah test ... ... ...

yang dianggap ideal? ... ... ...

Python – Unit Testing > Assert Statement > Well-tested Function 54


2. Assert Statement
Well-tested Function
Cara terbaik untuk menentukan jumlah test adalah dengan memilih beberapa dari setiap kategori
argumen berikut:
● Bad arguments
● Special arguments
● Normal argument
Apabila seluruh tipe argumen diatas sudah di-test, maka dapat dikatakan bahwa function tersebut
sudah well tested

Python – Unit Testing > Assert Statement > Well-tested Function


55
2. Assert Statement
Well-tested Function
Tipe 1: Bad Arguments merupakan argument yang akan menimbulkan exception.
Jika dilihat dari function split_into_training_and_testing_sets(), bad arguments-nya
adalah array satu dimensi. Ia tidak memiliki baris dan kolom, sehingga tidak bisa dilakukan splitting
baris. Outcome yang diekspektasikan adalah ValueError.
Array dengan satu baris juga merupakan bad argument karena testing arraynya akan kosong, atau
sebaliknya.
Argument Example Type Num rows Num exceptions
(training) rows
(testing)
One np.array([845.0, 31036.0, Bad - - ValueError
dimensional 1291.0,72205.0])

Contains 1 np.array([[845.0, 31036.0]]) Bad - - ValueError


row
Python – Unit Testing > Assert Statement > Well-tested Function
56
2. Assert Statement
Well-tested Function
Tipe 2: Special Arguments terbagi lagi menjadi 2 tipe, yaitu:
● Boundary Values
● Argument values → beberapa argument values, functionnya menggunakan special logic.

Apabila dilihat pada jumlah baris pada


argument, function akan menimbulkan
ValueError pada array dengan 1 baris.
Namun, akan berfungsi normal pada array
2 baris dan seterusnya.
Nilai 2 membatasi perubahan tersebut,
sehingga disebut sebagai boundary value.

Python – Unit Testing > Assert Statement > Well-tested Function


57
2. Assert Statement
Well-tested Function
Tipe 2: Special Arguments terbagi lagi menjadi 2 tipe, yaitu:
Tipe lainnya adalah yang membutuhkan special logic pada function. Contohnya pada array 4 kolom,
pembagian umum 75% training akan menghasilkan 3 baris training dan 1 baris array testing.
Namun, pengguna ingin agar dihasilkan 2 baris testing dan 2 baris training. Sehingga array 4 baris akan
termasuk ke special arguments.
Argument Type Num rows (training) Num rows (testing) exceptions

One dimensional Bad - - ValueError


Contains 1 row Bad - - ValueError
Contains 2 rows Special int(0.75 * 2) = 1 2 - int(0.75 * 2) = 1 -
Contains 4 rows Special 32 12 -

Python – Unit Testing > Assert Statement > Well-tested Function


58
2. Assert Statement
Well-tested Function
Tipe 3: Normal arguments adalah argument lainnya yang bukan bad maupun special arguments.
Pertama, karena 4 menggunakan special logic, maka 4 akan berbeda dengan argumen sebelum dan
sesudahnya. Hal ini menjadikan 3 dan 5 boundary values. Argument lainnya, yang lebih dari 5, adalah
normal arguments. Disarankan untuk melakukan test pada dua atau tiga normal arguments.

Python – Unit Testing > Assert Statement > Well-tested Function


59
2. Assert Statement
Well-tested Function
Argument Type Num rows (training) Num rows (testing) exceptions

One dimensional Bad - - ValueError

Contains 1 row Bad - - ValueError

Contains 2 rows Special int(0.75 * 2) =1 2 - int(0.75 * 2) =1 -

Contains 3 rows Special int(0.75 * 3) =2 3 - int(0.75 * 3) =1 -


Contains 4 rows Special 32 12 -
Contains 5 rows Special int(0.75 * 5) =3 5 - int(0.75 * 5) =2 -

Contains 6 rows Normal int(0.75 * 6) =4 6 - int(0.75 * 6) =2 -

Contains 8 rows Normal int(0.75 * 8) =6 8 - int(0.75 * 6) =2 -

Apabila dijalankan test pada argument di atas, maka dapat dikatakan function sudah well tested.

Python – Unit Testing > Assert Statement > Well-tested Function


60
2. Assert Statement
Well-tested Function
Namun, perlu diingat juga bahwa tidak semua function memiliki bad atau special arguments.
Sehingga, apabila terdapat function seperti itu, maka cukup dijalankan test pada tipe argumen yang
ada.

Python – Unit Testing > Assert Statement > Well-tested Function


61
2. Assert Statement
Test Driven Development
Sering kali kita melewatkan penulisan pada unit testing ini karena kita hanya terfokus pada penerapan fitur
dan hasil yang cepat tanpa melihat dampak jangka panjangnya. Selanjutnya kita akan membahas metode
coding dengan Test Driven Development (TDD).

Fungsi awal dapat dimulai dengan


pengimplementasian dan dilakukan
pengujian berdasarkan life cycle
tersebut

Python – Unit Testing > Assert Statement > Test Driven Development
62
2. Assert Statement
Test Driven Development

Test Driven Development biasanya dilakukan


dengan cara menambahkan penulisan unit
test sebelum dilakukan implementasi pada
life cycle tersebut.

Tujuan dilakukannya penulisan unit test


sebelum implementasi ini untuk memastikan
bahwa tahap tersebut tidak dapat ditunda
prioritasnya.

Python – Unit Testing > Assert Statement > Test Driven Development
63
2. Assert Statement
Test Driven Development

Penulisan unit test diimplementasikan dalam sebuah code. Dengan melakukan penulisan unit test kita
dapat memastikan bahwa:
● Penulisan unit test tidak dapat ditunda prioritasnya
● Pertimbangan waktu penulisan unit test bergantung pada waktu implementasinya
● Kemungkinan arguments dan return values yang mencakup normal, special, dan bad arguments

Python – Unit Testing > Assert Statement > Test Driven Development
64
2. Assert Statement
Test Driven Development
Kita akan mengaplikasikan metode Test Driven Development dalam function convert_to_int()

convert_to_int("2,081")

2081

Fungsi ini mengkonversi string yang bernilai integer dengan


koma sebagai pemisah ribuan menjadi integer

Python – Unit Testing > Assert Statement > Test Driven Development
65
2. Assert Statement
Test Driven Development
Step 1: Menuliskan unit test dan menetapkan requirements

Test module: test_convert_to_int.py kita akan melalui 3 proses dalam menetapkan


fungsinya
import pytest
Pertama, tulis unit test untuk fungsi ini di test
def test_with_no_comma():
... module test_convert_to_int.py.
Kemudian kita dapat mendefinisikan pytest
def test_with_one_comma():
... sebagai unit test

def test_with_two_commas(): Selanjutnya dilakukan pengujian apakah pytest


... menghasilkan nilai yang sesuai untuk no comma,
one comma, dan two commas secara berurutan.

Python – Unit Testing > Assert Statement > Test Driven Development
66
2. Assert Statement
Test Driven Development
Step 2: Jalankan Unit Test dan Lihat Kegagalannya

!pytest test_convert_to_int.py

Jalankan unit test dengan


memanggil !pytest serta
test module yang telah
dibuat.

Ketika test module dijalankan


pastinya akan terjadi
kegagalan karena fungsinya
belum ada.

Python – Unit Testing > Assert Statement > Test Driven Development
67
2. Assert Statement
Test Driven Development
Step 3: Implementasikan Fungsi dan Jalankan Ulang Unit Test
def convert_to_int(): Terakhir, implementasikan fungsi
... dan jalankan ulang test.
!pytest test_convert_to_int.py
Bagian ini menampilkan
informasi bahwa fungsi yang
telah diimplementasikan sudah
benar.

Sebaliknya, jika terjadi kegagalan


kita dapat memperbaiki bugs
dan mengulang kembali step ini

Python – Unit Testing > Assert Statement > Test Driven Development
68
2. Assert Statement
Test Driven Development

Output tertulis “collected 6 items” menandakan bahwa ditemukan 6 unit test yang akan dijalankan.
Baris selanjutnya memuat nama test module, yaitu test_convert_to_int.py, tanpa diikuti “.F.” yang
berarti hasil unit test tidak mengalami kegagalan.

Python – Unit Testing > Assert Statement > Test Driven Development
69
Topik 3 – Mengelola
Kumpulan Tests

Python – Unit Testing > Mengelola Kumpulan Test


70
3. Mengelola Kumpulan Test
Pengantar
Untuk menguji suatu function, tentu memerlukan unit test yang tidak sedikit. Seiring dengan
bertambahnya jumlah unit test, maka diperlukan strategi untuk mengelola kumpulan unit test
tersebut. Jika tidak, maka penggunaan unit test akan berantakan dan mengganggu jalannya pengujian
suatu function.
Apabila dianalogikan, tests yang berantakan ini terlihat seperti ilustrasi baju di samping.

Python – Unit Testing > Mengelola Kumpulan Test > Pengantar


71
3. Mengelola Kumpulan Test
Pengantar
Perhatikan contoh struktur project yang memuat function yang perlu di-test.
src/
# src menyimpan seluruh code
|-- data/
| |-- init .py # Package untuk preprocessing data
| |-- preprocessing_helpers.py
# Menyimpan function row_to_list(), convert_to_int()
|-- features/
# Package untuk membuat fitur dari preprocessed data
| |-- init .py
| |-- as_numpy.py # Menyimpan function get_data_as_numpy_array()
|-- models/
| |-- init .py # Package untuk training dan testing linear regression model
| |-- train.py # Menyimpan function split_into_training_and_testing_sets(
tests/ # Directory bernama tests, atau disebut juga test suite
|-- data/ # Tersusun dari package dan function yang sama persis dengan src
| |-- init .py
| |-- test_preprocessing_helpers.py
Konsep umumnya adalah tiap modul harus berkorespondensi ke test modulenya
|-- features/
masing-masing. Contoh, preprocessing_helpers.py yang terletak di data package,
| |-- init .py
berkorespondensi tepat dengan test_preprocessing_helpers.py yang terletak di
|-- models/
cerminan directorynya yaitu data package dalam tests directory.
|-- init .py

Python – Unit Testing > Mengelola Kumpulan Test > Pengantar


72
3. Mengelola Kumpulan Test
Pengantar
import pytest Dalam test module, pengguna
from data.preprocessing_helpers bisa saya menyusun testsnya
import row_to_list, convert_to_int secara berurutan seperti pada
def test_on_no_tab_no_missing_value(): # A test for row_to_list() contoh. Namun, hal ini sangat
... tidak teratur dan menyulitkan
karena pengguna tidak bisa
def test_on_two_tabs_no_missing_value(): # Another test for row_to_list() secara intuitif mengetahui di
... mana test untuk suatu function
berarkhir dan test untuk function
...
lainnya dimulai.
def test_with_no_comma(): # A test for convert_to_int()
...

def test_with_one_comma(): # Another test for convert_to_int()


...

...

Python – Unit Testing > Mengelola Kumpulan Test > Pengantar


73
3. Mengelola Kumpulan Test
Pengantar
pytest menangani permasalahan struktur test module tersebut dengan
membangun test class. Test class adalah container sederhana untuk seluruh tests
pada suatu function tertentu.

import pytest Untuk memulai, gunakan keyword class yang


from data.preprocessing_helpers disusul dengan nama dari kelas tersebut. Nama
kelas harus menggunakan KapitalSetiapKata
import row_to_list, convert_to_int
dengan Test sebagai awalannya. Cara ideal untuk
memberi nama kelas adalah dengan menuliskan
class TestRowToList():
nama functionnya setelah kata Test, contoh
TestRowToList.

Python – Unit Testing > Mengelola Kumpulan Test > Pengantar


74
3. Mengelola Kumpulan Test
Pengantar
import pytest Test class memerlukan satu argument yang
from data.preprocessing_helpers disebut object. Sekarang, tuliskan seluruh test
import row_to_list, convert_to_int untuk masing-masing function di bawah test
class seperti pada contoh.
class TestRowToList(object):
def test_on_no_tab_no_missing_value(self): Seluruh test memerlukan satu argument yang
... disebut self.
def test_on_two_tabs_no_missing_value(self):
...

Untuk function convert_to_int(), dibuat


class TestConvertToInt(object):
def test_with_no_comma(self): class baru TestConvertToInt, dan
... menuliskan seluruh test di dalam class tersebut.
def test_with_one_comma(self):
Sekarang tests untuk kedua function sudah
...
dipisah dengan rapi.

Python – Unit Testing > Mengelola Kumpulan Test > Pengantar


75
3. Mengelola Kumpulan Test
Pengantar
Apabila seluruh test untuk masing-masing function sudah dibuat classnya tersendiri, maka dapat
dikatakan tests yang dibuat sudah tersusun dengan baik.

Python – Unit Testing > Mengelola Kumpulan Test > Pengantar


76
3. Mengelola Kumpulan Test
Test Execution
Test execution merupakan test yang dilakukan dengan cara menjalankan sistem yang diuji untuk
memperoleh hasil aktual yang dapat dibandingkan dengan hasil yang diharapkan untuk menentukan
apakah pengujian telah lolos atau mengalami kegagalan.
Untuk menguasai test execution dalam mengelola kumpulan test, kita harus mengetahui Test
Organization yang akan dijelaskan pada slide selanjutnya

Python – Unit Testing > Mengelola Kumpulan Test > Test Execution
77
3. Mengelola Kumpulan Test
Test Execution
Test Organization

Tests folder bertujuan untuk menampung


seluruh test dalam project

Folder tersebut berisi mirror packages yang masing-masing berisi test module

Python – Unit Testing > Mengelola Kumpulan Test > Test Execution
78
3. Mengelola Kumpulan Test
Test Execution
Test Organization

Test modules juga berisi


beberapa test classes

Test classes sebagai unit test


yang digunakan untuk fungsi
tertentu
Python – Unit Testing > Mengelola Kumpulan Test > Test Execution
79
3. Mengelola Kumpulan Test
Test Execution
Pytest menyediakan cara mudah untuk
menjalankan seluruh test dalam test
folder

Python – Unit Testing > Mengelola Kumpulan Test > Test Execution
80
3. Mengelola Kumpulan Test
Test Execution
cd tests
pytest

● Ubah directory subtree ke tests/


○ Seluruh file dengan nama yang diawali dengan test_ merupakan test module.
■ Classnames yang diawali test_ → test class.
■ Fungsi yang diawali test_ → unit test.

Command tersebut secara otomatis menemukan tests dengan mengulang ke subtree dari direktori
dan mengidentifikasi seluruh file dengan nama yang dimulai dengan “test_” sebagai test modul.
Selanjutnya unit test ini dikumpulkan dan dijalankan seluruhnya secara bersamaan.

Python – Unit Testing > Mengelola Kumpulan Test > Test Execution
81
3. Mengelola Kumpulan Test
Test Execution

Berikut merupakan hasil dari command pytest. Test yang ditulis sebanyak 16, dengan keterangan 15
berhasil dan 1 gagal.

Python – Unit Testing > Mengelola Kumpulan Test > Test Execution
82
3. Mengelola Kumpulan Test
Test Execution

Umumnya, command dijalankan di dalam CI server setelah commit di-push ke code base.
Pertanyaannya, apakah seluruh unit test pasti berhasil dilewati?

Dalam hal ini, menambahkan -x ke dalam pytest command


dapat menghemat banyak waktu dan resource. Flag ini
menginstruksikan pytest untuk berhenti setelah fail pertama
dalam test, karena satu saja failure dalam test tentu sudah
bisa menjawab pertanyaan di atas tersebut dan unit testing
tidak perlu diselesaikan hingga akhir.

Python – Unit Testing > Mengelola Kumpulan Test > Test Execution
83
3. Mengelola Kumpulan Test
Test Execution
pytest -x

Hasil dari command test,


kita melihat bahwa hanya 9
test yang berjalan, proses
eksekusi berhenti setelah
terjadi kegagalan pada
test_on_one_tab_with_
missing_value().

Python – Unit Testing > Mengelola Kumpulan Test > Test Execution
84
3. Mengelola Kumpulan Test
Test Execution

Tak jarang kita hanya ingin menjalankan


sebagian dari test yang terdapat dalam modul
tertentu misalnya,
test_preprocessing_helpers.py.
Tentunya kita sudah tahu bagaimana cara
melakukannya karena sudah beberapa kali
dilakukan dalam latihan.

Python – Unit Testing > Mengelola Kumpulan Test > Test Execution
85
3. Mengelola Kumpulan Test
Test Execution
pytest data/test_preprocessing_helpers.py

Cukup ketik pytest diikuti


dengan path ke modul test.
Command Ini hanya
menjalankan 13 tes yang
ada di
test_preprocessing_he
lpers.py, dengan hasil
yang dapat kita lihat pada
report tersebut.

Python – Unit Testing > Mengelola Kumpulan Test > Test Execution
86
3. Mengelola Kumpulan Test
Test Execution

Spesifiknya, saat kita mengerjakan fungsi tertentu,


misalnya row_to_list(), kita hanya perlu
memperhatikan test class pada TestRowToList.

Python – Unit Testing > Mengelola Kumpulan Test > Test Execution
87
3. Mengelola Kumpulan Test
Test Execution

Pytest menetapkan node ID ke setiap test class dan unit test yang ditemuinya. Node ID dari test class
merupakan path ke module test yang diikuti dengan nama test class, dipisahkan oleh dua titik dua.
Penulisan node ID dari unit test juga mengikuti format yang sama dengan nama unit test ditambahkan di
bagian akhir menggunakan pemisah titik dua ganda lainnya

Node ID dari test class: <path to test module>::<test class name>

Node ID dari unit test: <path to test module>::<test class name>

Python – Unit Testing > Mengelola Kumpulan Test > Test Execution
88
3. Mengelola Kumpulan Test
Test Execution
● Menjalankan test class: TestRowToList
pytest data/test_preprocessing_helpers.py::TestRowToList

Saat kita menjalankan


command pytest diikuti
dengan ID node dari test
class TestRowToList, kita
melihat bahwa hanya 7 test
pada TestRowToList yang
berjalan

Python – Unit Testing > Mengelola Kumpulan Test > Test Execution
89
3. Mengelola Kumpulan Test
Test Execution
● Menjalankan unit class: test_on_one_tab_with_missing_value()
pytest data/test_preprocessing_helpers.py::TestRowToList::test_on_one_tab_with_missing_value

Saat kita menjalankan


command dengan node ID
dari unit test
test_on_one_tab_with
_missing_value,
kita melihat bahwa hanya
satu test yang berjalan.

Python – Unit Testing > Mengelola Kumpulan Test > Test Execution
90
3. Mengelola Kumpulan Test
Test Execution
Selain dengan node ID, kita juga dapat melakukan dengan cara yang lebih cepat dan fleksibel
menggunakan keyword expressions.
pytest -k "pattern"

Untuk menjalankan test menggunakan keyword expressions, kita dapat menggunakan -k option.
Opsi ini mengambil string yang dikutip berisi pattern sebagai valuenya.

Python – Unit Testing > Mengelola Kumpulan Test > Test Execution
91
3. Mengelola Kumpulan Test
Test Execution
● Menjalankan test class: TestSplitIntoTrainingAndTestingSets
pytest -k "TestSplitIntoTrainingAndTestingSets"

Kita dapat menentukan test class dengan TestSplitIntoTrainingAndTestingSets sebagai patternnya,


dan dapat kita ketahui hanya 2 test yang berjalan dalam test class tersebut.

pytest -k "TestSplit"

Kita juga dapat memasukkan sebagian dari nama test class saja, asalkan nama tersebut unik. proses ini
dapat menghemat banyaknya penulisan dengan output yang sama.

Python – Unit Testing > Mengelola Kumpulan Test > Test Execution
92
3. Mengelola Kumpulan Test
Test Execution

pytest -k "TestSplit and not test_on_one_row"

Bahkan kita juga dapat menggunakan operator logika Python dalam pattern untuk melakukan subset
yang lebih kompleks. Misalnya, pada command berikut akan menjalankan semua test di
TestSplitIntoTrainingAndTestingSets kecuali unit test test_on_one_row(), yang hanya
menyisakan satu test untuk dijalankan.

Python – Unit Testing > Mengelola Kumpulan Test > Test Execution
93
3. Mengelola Kumpulan Test
Expected Failures dan Conditional Skipping
Jika test berhasil dilalui, maka test suite akan berwarna hijau dan kita sudah bisa bersantai
menggunakan function tersebut.
Jika test gagal, maka test suite akan berwarna merah dan pengguna masih harus memperbaiki function
tersebut.
Namun, seringkali warna merah pada test suite merupakan false alarm yang mengganggu.

Python – Unit Testing > Mengelola Kumpulan Test > Expected Failures dan Conditional Skipping
94
3. Mengelola Kumpulan Test
Expected Failures dan Conditional Skipping
Apabila pengguna menjalankan pytest, akan dihasilkan error karena train_model() belum
diimplementasikan.
Hal ini hanyalah akibat dari
penggunaan TDD, bukan
mengindikasikan masalah
pada code.

Server CI tidak mengetahui


hal tersebut dan akan tetap
menganggap test gagal,
maka dihasilkanlah fail false
alarm.

Python – Unit Testing > Mengelola Kumpulan Test > Expected Failures dan Conditional Skipping
95
3. Mengelola Kumpulan Test
Expected Failures dan Conditional Skipping
Pengguna bisa mengatur pytest agar memperlakukan hal tersebut sebagai kesalahan yang sudah
diantisipasi, atau expected failure, dengan menggunakan xfail decorator.

import pytest xfail decorator ditempatkan di atas


test, diawali dengan karakter @ diikuti
class TestTrainModel(object): dengan nama dari decorator
@pytest.mark.xfail pytest.mark.xfail.
def test_on_linear_data(self):
Setelah menambahkan decorator,
... kemudian menjalankan pytest kembali,
pytest
dapat dilihat bahwa satu test is xfailed
namun tidak menimbulkan error,
sehingga suite akan tetap berwarna
hijau.

Python – Unit Testing > Mengelola Kumpulan Test > Expected Failures dan Conditional Skipping
96
3. Mengelola Kumpulan Test
Expected Failures dan Conditional Skipping
Terkadang, terdapat suatu tests yang gagal hanya apabila berada di suatu kondisi tertentu. Pada hal
inipengguna tidak perlu diberikan informasi error terkait. Situasi tersebut biasanya terjadi pada saat
suatu funtion tidak mau bekerja di bawah python versi tertentu.
class TestConvertToInt(object):
def test_with_no_comma(self):
"""Only runs on Python 2.7 or lower""" test_argument = "756"
expected = 756
actual = convert_to_int(test_argument)
message = unicode("Expected: 2081, Actual: {0}".format(actual))
assert actual == expected, message

Sebagai contoh, ditambahkan function unicode() dalam failure message yang menunjukkan bahwa
function tersebut hanya bisa dijalankan pada python versi 2.7 atau dibawahnya.

Python – Unit Testing > Mengelola Kumpulan Test > Expected Failures dan Conditional Skipping
97
3. Mengelola Kumpulan Test
Expected Failures dan Conditional Skipping
Jika pengguna menjalankan pytest pada Python 3, test suite akan berwarna merah.
pytest

Python – Unit Testing > Mengelola Kumpulan Test > Expected Failures dan Conditional Skipping
98
3. Mengelola Kumpulan Test
Expected Failures dan Conditional Skipping
Untuk memberitahu pytest agar melewati / skip penjalanan test pada Python apabila Python memiliki
versi lebih tinggi dari 2.7, digunakan decorator skipit. Syntax skipit mirip dengan xfail. Nama dari
decorator-nya adalah pytest.mark.skipif
class TestConvertToInt(object): Argumennya
@pytest.mark.skipif(boolean_expression) berupa boolean
def test_with_no_comma(self): expression, di
"""Only runs on Python 2.7 or lower"""
mana apabila
True maka test
test_argument = "756"
tersebut akan
expected = 756
diskip.
actual = convert_to_int(test_argument)
message = unicode("Expected: 2081, Actual: {0}".format(actual))

assert actual == expected, message


Python – Unit Testing > Mengelola Kumpulan Test > Expected Failures dan Conditional Skipping
99
3. Mengelola Kumpulan Test
Expected Failures dan Conditional Skipping
Boolean expression didefinisikan dengan pertama-tama import module sys, dan gunakan atribut
sys.version_info. Atribut ini dapat dibandingkan dengan tuple yang berisi versi Python, yaitu 2
(major) dan 7 (minor). Tambahkan pula alasan mengapa test tersebut dilewati.
import sys

class TestConvertToInt(object):
@pytest.mark.skipif(sys.version_info> (2, 7), reason="requires Python 2.7")
def test_with_no_comma(self):
"""Only runs on Python 2.7 or lower""" test_argument = "756"
expected = 756
actual = convert_to_int(test_argument)
message = unicode("Expected: 2081, Actual: {0}".format(actual))
assert actual == expected, message

Python – Unit Testing > Mengelola Kumpulan Test > Expected Failures dan Conditional Skipping
100
3. Mengelola Kumpulan Test
Expected Failures dan Conditional Skipping
Apabila menjalankan pytest lagi maka akan terkonfirmasi bahwa ada satu test yang xfailed dan ada
satu test yang dilewati. Test suite akan tetap berwarna hijau.

pytest

Python – Unit Testing > Mengelola Kumpulan Test > Expected Failures dan Conditional Skipping
101
3. Mengelola Kumpulan Test
Expected Failures dan Conditional Skipping
Alasan dari skipping test dapat ditampilkan di report dengan menuliskan -r option. -r option bisa juga
disertai dengan karakter.
pytest -r[set_of_characters] Apabila ditambahkan
karakter s, maka tests yang
dilewati akan ditampilkan
pada bagian rangkuman test
kecil di akhir report.

Python – Unit Testing > Mengelola Kumpulan Test > Expected Failures dan Conditional Skipping
102
3. Mengelola Kumpulan Test
Expected Failures dan Conditional Skipping
xfail decorator juga memiliki argumen tambahan reason

import pytest Pada .xfail ditambahkan argumen


reason yang menjelaskan alasan
class TestTrainModel(object):
mengapa fail pada test tersebut sudah
@pytest.mark.xfail(reason="“Using TDD, diekspektasikan
train_model() is not implemented")
def test_on_linear_data(self):

...

Python – Unit Testing > Mengelola Kumpulan Test > Expected Failures dan Conditional Skipping
103
3. Mengelola Kumpulan Test
Expected Failures dan Conditional Skipping

Apabila ditambahkan karakter x pada option -r, maka Apabila ditambahkan karakter s dan x, maka akan
akan ditampilkan test yang xfailed beserta alasannya. ditampilkan alasan untuk xfailed dan skipped test

pytest -rx pytest -rsx

Python – Unit Testing > Mengelola Kumpulan Test > Expected Failures dan Conditional Skipping
104
3. Mengelola Kumpulan Test
Expected Failures dan Conditional Skipping
xfail dan skip test dapat dilakukan pada lebih dari satu tests, dengan menempatkan decorators ke
seluruh test class dengan menuliskannya di atas class.
Contoh menerapkan xfail pada seluruh tests di dalam class:
@pytest.mark.xfail(reason="“Using TDD, train_model() is not implemented") class
TestTrainModel(object):

...

Contoh menerapkan skipif pada seluruh tests di dalam class:


@pytest.mark.skipif(sys.version_info > (2, 7), reason="requires Python 2.7")

class TestConvertToInt(object):

...

Python – Unit Testing > Mengelola Kumpulan Test > Expected Failures dan Conditional Skipping
105
3. Mengelola Kumpulan Test
Continuous Integration dan Cove Coverage
Pada topik pertama, telah dipelajari bagaimana NumPy meningkatkan
kepercayaan penggunanya dengan badges yang ditampilkan pada laman
GitHubnya.

Badges tersebut menggunakan continuous integration server yang


menjalankan seluruh tests secara otomatis kapanpun pengguna menekan
commit pada GitHub. CI server menunjukkan apakah tests tersebut berhasil
atau gagal. Code yang berhasil melalui test akan dianggap stabil oleh
pengguna, sementara code yang gagal akan dianggap tidak stabil.

Pada modul kali ini, akan digunakan Travis CI sebagai CI server.

Python – Unit Testing > Mengelola Kumpulan Test > Continuous Integration dan Cove Coverage
106
3. Mengelola Kumpulan Test
Continuous Integration dan Cove Coverage
Untuk mengintegrasikan sistem dengan Travis CI, pertama-tama buat file settings dengan
nama .travis.yml di root repository. File tersebut disusun dalam bagian-bagian sebagai berikut.
1. Language/bahasa pemrograman yang diatur menjadi
language: python python:
Python
2. Python untuk mendefinisikan versi Python yang - "3.6"
digunakan install:
3. Install berisi daftar commands untuk menginstall
- pip install -e . script:
project dan dependencies ke dalam CI server
4. Script berisi commands untuk menjalankan test - pytest tests

git add .travis.yml Setting file tersebut kemudian di-push ke


git push origin master Github

Python – Unit Testing > Mengelola Kumpulan Test > Continuous Integration dan Cove Coverage
107
3. Mengelola Kumpulan Test
Continuous Integration dan Cove Coverage
Setelah file setting di-push ke GitHub, selanjutnya Install Travis CI dengan langkah-langkah berikut:

Buka MarketPlace pada GitHub Cari dan Install Travis CI

Beri izin app untuk mengakses repository atau


organization yang dibutuhkan

Python – Unit Testing > Mengelola Kumpulan Test > Continuous Integration dan Cove Coverage
108
3. Mengelola Kumpulan Test
Continuous Integration dan Cove Coverage
Kemudian kita akan diarahkan ke Travis CI, login menggunakan akun GitHub, dan menampilkan
dashboard Travis CI.

Setiap kali kita menekan commit ke


repo GitHub, akan muncul build di
dashboard Travis CI.

Setelah build selesai, akan muncul


badge di pojok kanan atas.

Python – Unit Testing > Mengelola Kumpulan Test > Continuous Integration dan Cove Coverage
109
3. Mengelola Kumpulan Test
Continuous Integration dan Cove Coverage
Berikut merupakan tampilan seteleh badge di-klik.

Pilih markdown, kemudian paste


markdown tersebut ke README file
di GitHub. Hal ini akan
memunculkan badges ke dalam
GitHub repo tersebut.

Python – Unit Testing > Mengelola Kumpulan Test > Continuous Integration dan Cove Coverage
110
3. Mengelola Kumpulan Test
Continuous Integration dan Cove Coverage
Selanjutnya adalah cara menampilkan code coverage badge. Code coverage
badge mengindikasikan seberapa sering code dilakukan test Nilai persentase
yang tinggi (di atas 75%) menunjukkan code yang well-tested.

language: python python: Step 1: Modifikasi File Konfigurasi Travis CI


- "3.6" Lakukan modifikasi file .travis.yml untuk meng-enable code
install:
coverage reports.
- pip install -e .
- pip install pytest-cov codecov
● Pada install setting, tambahkan pip install pytest-cov and
script: codevov
- pytest --cov=src tests ● Script ditambahkan –cov = src tests
after_success: ● Penambahan setting after_success dan tambahkan
- codecov
command codecov

Python – Unit Testing > Mengelola Kumpulan Test > Continuous Integration dan Cove Coverage
111
3. Mengelola Kumpulan Test
Continuous Integration dan Cove Coverage
Step 2: Install Codecov. Untuk menambahkan codecov di repository pengguna, install Codevow app di
GitHub marketplace seperti yang sebelumnya sudah dilakukan untuk Travis CI.

Python – Unit Testing > Mengelola Kumpulan Test > Continuous Integration dan Cove Coverage
112
3. Mengelola Kumpulan Test
Continuous Integration dan Cove Coverage
Ketika pengguna menekan commit, code coverage report akan muncul di Codecov, dapat di akses di
codecov.io, setelah Travis CI menyelesaikan build-nya.

Python – Unit Testing > Mengelola Kumpulan Test > Continuous Integration dan Cove Coverage
113
3. Mengelola Kumpulan Test
Setup dan Teardown
Perhatikan function preprocess() sebagai contoh.

1,801 201,411 Function pertama-tama memproses baris


1,767565,112 data kedua yang tidak memiliki tab dengan
2,002 333,209 menggunakan row_to_list().
1990 783,911
row_to_list()
1,285 389129
Kemudian, baris keempat dan kelima tidak
def preprocess(raw_data_file_path, memiliki koma, sehingga di-filter dengan
clean_data_file_path convert_to_int()
): convert_to_int(). Baris pertama dan kedua
. . . dikonversi menjadi integers.

1801 201411 clea


2022 333209 n Hasil akhir berupa file yang sudah clean

Python – Unit Testing > Mengelola Kumpulan Test > Setup dan Teardown
114
3. Mengelola Kumpulan Test
Setup dan Teardown
preprocess() berbeda dengan function lainnya karena ia memiliki suatu precondition untuk
berjalan dengan benar. Preconditionnya berupa adanya raw data file di dalam environment.

Perbedaan lainnya adalah ketika kita menggunakan


functionnya, maka akan terbentuk data baru clean data
raw clean di dalam environment tersebut.

The environment

Python – Unit Testing > Mengelola Kumpulan Test > Setup dan Teardown
115
3. Mengelola Kumpulan Test
Setup dan Teardown
Untuk function preprocess, perlu dibuat test
def test_on_raw_data(): # Setup: create the
raw data file
test_on_raw_data().
preprocess(raw_data_file_path, Step 1, buat file raw data terlebih dahulu. Step ini
clean_data_file_path )
disebut sebagai setup, yakni mempersiapkan
environment hingga siap untuk dilakukan testing.
with open(clean_data_file_path) as
f: lines = f.readlines() Step 2 Assert. Kemudian function preprocess()
first_line = lines[0] digunakan untuk menghasilkan file baru clean data. Kita
assert first_line == "1801\t201411\ perlu membuka file tersebut, membacanya, dan
n" second_line = lines[1] melakukan assert bahwa file tersebut sudah berisi
assert second_line == "2002\t333209\n" informasi yang benar.

# Teardown: remove raw and clean data file Step 3 Teardown. File raw dan clean data dihapus dari
environment untuk mengembalikan environment ke
dalam kondisi awal.
Python – Unit Testing > Mengelola Kumpulan Test > Setup dan Teardown
116
3. Mengelola Kumpulan Test
Setup dan Teardown
Kesimpulannya, melainkan menggunakan assert statement secara sequential, kita perlu menggunakan
workflow baru Setup → Assert → Teardown
import pytest
Dalam pytest, setup dan teardown berada di luar test,
melainkan merupakan bagian dari
@pytest.fixture
function .fixture.
def my_fixture():
# Do setup here Bagian pertama merupakan setup, kemudian
yield data menghasilkan data yang akan digunakan pada test.
# Do teardown here Fixture menggunakan yield keyword.
def test_something(my_fixture):
Selanjutnya merupakan bagian teardown yang akan
...
dijalankan setelah test selesai.
data = my_fixture
Test dapat mengakses data tersebut dengan
...
mendefinisikan my_fixture sebagai data.

Python – Unit Testing > Mengelola Kumpulan Test > Setup dan Teardown
117
3. Mengelola Kumpulan Test
Setup dan Teardown
@pytest.fixture def test_on_raw_data(raw_and_clean_data_file):
def raw_and_clean_data_file(): raw_path, clean_path = raw_and_clean_data_file
raw_data_file_path = "raw.txt" preprocess(raw_path, clean_path)
clean_data_file_path = "clean.txt" with open(clean_data_file_path)
with open(raw_data_file_path, "w") as f: as f: lines =
f.write("1,801\t201,411\n" f.readlines()
"1,767565,112\n"
first_line = lines[0]
"2,002\t333,209\n"
assert first_line == "1801\
"1990\t782,911\n"
t201411\n" second_line =
"1,285\t389129\n" )
lines[1]
yield raw_data_file_path, clean_data_file_path
assert second_line == "2002\t333209\n"
os.remove(raw_data_file_path)
os.remove(clean_data_file_path

Berikut merupakan fixture raw_and_clean_data_file() yang telah dibuat. Dalam setup, dituliskan path dari raw dan
clean data. Kemudian, akan dilanjutkan dengan assert statement dari test test_on_raw_data(). Dalam teardown,
raw dan clean data dihapuskan menggunakan os.remove() function.
Python – Unit Testing > Mengelola Kumpulan Test > Setup dan Teardown
118
3. Mengelola Kumpulan Test
Setup dan Teardown
Terdapat built-in pytest fixture tmpdir, yang berguna saat sedang mempersiapkan file. tmpdir membuat
direktori sementara selama setup yang akan terhapus saat teardown.
@pytest.fixture
def raw_and_clean_data_file(tmpdir):
fixture tmpdir dapat digunakan sebagai
raw_data_file_path = tmpdir.join("raw.txt")
argument di dalam fixture
clean_data_file_path = tmpdir.join("clean.txt") raw_and_clean_data_file, atau disebut juga
with open(raw_data_file_path, "w") as f: sebagai fiture chaining.
f.write("1,801\t201,411\n" Setup tmpdir dijalankan terlebih dahulu
"1,767565,112\n"
kemudian baru setup dari fixture kita.
"2,002\t333,209\n"
"1990\t782,911\n" Pengguna dapat menghilangkan bagian
"1,285\t389129\n") teardown dari fixture karena sudah dihapuskan
yield raw_data_file_path, clean_data_file_path pada fixture tmpdir.
# No teardown code necessary

Python – Unit Testing > Mengelola Kumpulan Test > Setup dan Teardown
119
3. Mengelola Kumpulan Test
Mocking
Mocking merupakan cara untuk menguji function secara independen, terlepas dari dependencies yang
dimilikinya. Untuk menggunakan mocking, diperlukan dua package yaitu pytest-mock dan
unittest.mock

raw Menguji function secara independen yaitu


menguji function preprocess(),
bukan dependencies row_to_list()
atau convert_to_int()
row_to_list()

convert_to_int()

clean

Python – Unit Testing > Mengelola Kumpulan Test > Mocking


120
3. Mengelola Kumpulan Test
Mocking
Ide dasar dari mocking adalah mengganti dependencies yang berpotensi bug dengan object
unittest.mock.MagicMock() selama test berlangsung.

def test_on_raw_data(raw_and_clean_data_file,
mocker,):
raw_path, clean_path = raw_and_clean_data_file
row_to_list_mock = mocker.patch(...)

Pergantian ini dilakukan menggunakan


fixture mocker, dan memanggil patch
method pada awal test
test_on_raw_data().

Python – Unit Testing > Mengelola Kumpulan Test > Mocking


121
3. Mengelola Kumpulan Test
Mocking
mocker.patch("<dependency name with module name>")

Argument pertama dari mocker.patch adalah nama dari dependencies termasuk nama modulenya.
preprocess() mengenal
def test_on_raw_data(raw_and_clean_data_file, row_to_list() sebagai
mocker, ): data.preprocessing_helpers.
raw_path, clean_path = raw_and_clean_data_file row_to_list sehingga itulah yang
row_to_list_mock = akan dituliskan di patch.
mocker.patch( "data.preprocessing_helpers.row_to_lis mocker.patch() method
t") menghasilkan MagicMock object yang
unittest.mock.MagicMock() disimpan ke dalam variabel
row_to_list_mock.

Python – Unit Testing > Mengelola Kumpulan Test > Mocking


122
3. Mengelola Kumpulan Test
Mocking
def row_to_list_bug_free(): return_values = { Selama test, row_to_list_mock akan diprogram agar
"1,801\t201,411\n": ["1,801", "201,411"],
bertindak seperti pengganti row_to_list() yang tidak
"1,767565,112\n": None,
memiliki bug, versi ini akan kita sebut dengan
"2,002\t333,209\n": ["2,002", "333,209"],
"1990\t782,911\n": ["1990", "782,911"],
row_to_list_bug_free(). Perlu diingat bahwa ini hanya
"1,285\t389129\n": ["1,285", "389129"], perlu bug-free di dalam konteks test, sehingga akan lebih
} simpel dibandingkan function aslinya row_to_list().
return return_values[row]

def test_on_raw_data(raw_and_clean_data_file, row_to_list_bug_free() hanya perlu memberikan


mocker, ): hasil yang sesuai untuk lima baris yang
raw_path, clean_path = raw_and_clean_data_file didefinisikan saja. Maka, dibuat dictionary yang
row_to_list_mock = berisi hasil yang sesuai untuk masing-masing baris
mocker.patch( "data.preprocessing_helpers.row_to_list")
tersebut. Kemudian kita tuliskan side_effect()
row_to_list_mock.side_effect = row_to_list_bug_free
attribute ke dalam versi bug-free.

Python – Unit Testing > Mengelola Kumpulan Test > Mocking


123
3. Mengelola Kumpulan Test
Mocking
Sekarang, apabila kita melakukan uji preprocess, maka yang akan digunakan adalah bug-free mock dari
row_to_list(), dan test tidak akan menemukan bugs apapun.

def test_on_raw_data(raw_and_clean_data_file,mocker, ):
raw_path, clean_path = raw_and_clean_data_file
row_to_list_mock = mocker.patch(
"data.preprocessing_helpers.row_to_list",
side_effect = row_to_list_bug_free)
preprocess(raw_path, clean_path)

Python – Unit Testing > Mengelola Kumpulan Test > Mocking


124
3. Mengelola Kumpulan Test
Mocking
Pengguna juga bisa melakukan cek apakah preprocess() memanggil row_to_list() dengan argumen yang benar.
call_args_list adalah atribut yang menyimpan seluruh argumen yang dipanggil oleh row_to_list_mock,
disimpan dalam object call(). Kemudian kita bisa melakukan assert pada call_args_list.
from unittest.mock import call
row_to_list_mock.call_args_list

def test_on_raw_data(raw_and_clean_data_file,mocker, ):
[call("1,801\t201,411\n"),
raw_path, clean_path = raw_and_clean_data_file
call("1,767565,112\n"), row_to_list_mock = mocker.patch(
call("2,002\t333,209\n"), "data.preprocessing_helpers.row_to_list",
call("1990\t782,911\n"), side_effect = row_to_list_bug_free)
call("1,285\t389129\n") preprocess(raw_path, clean_path)
] assert row_to_list_mock.call_args_list == [ call("1,801\
t201,411\n"), call("1,767565,112\n"),
call("2,002\t333,209\n"), call("1990\t782,911\n"),
call("1,285\t389129\n") ]

Python – Unit Testing > Mengelola Kumpulan Test > Mocking


125
3. Mengelola Kumpulan Test
Mocking
Berikut merupakan contoh di mana row_to_list() mengandung bug namun preprocess() tidak. Jika kita
jalankan test pada kedua function, dapat dilihat row_to_list() akan gagal namun preprocess() tetap berhasil.
Hal ini lah yang diinginkan.
pytest -k "TestRowToList" pytest -k "TestPreprocess"

Python – Unit Testing > Mengelola Kumpulan Test > Mocking


126
3. Mengelola Kumpulan Test
Testing Model
Sejauh ini, kita sudah mempelajari dan menguji function-function berikut:
● preprocess() - untuk mempersiapkan raw data housing_data.txt
● get_data_as_numpy_array() - untuk mengambil data dan menyimpannya ke dalam NumPy aray
● split_into_training_and_testing_set() - untuk membagi data menjadi training dan testing secara acak.

Sekarang saatnya melakukan train model linear regression menggunakan function train_model().
from scipy.stats import linregress
linregress() melakukan regresi
linear terhadap dua kolom.
def train_model(training_set):
slope, intercept, _, _, _ = linregress(training_set[:, 0],
training_set[:, 1])
return slope, intercept

Python – Unit Testing > Mengelola Kumpulan Test > Testing Model
127
3. Mengelola Kumpulan Test
Testing Model
Function train_model() melakukan proses regresi linear yang kompleks, sehingga kita tidak bisa secara
mudah mengetahui prediksi best fit line. Dan karena kita tidak mengetahui return value yang
diekspektasikan, kita tidak bisa menguji function tersebut. Hal ini berlaku tidak hanya pada linear
regression, namun juga seluruh model data science.

Python – Unit Testing > Mengelola Kumpulan Test > Testing Model
128
3. Mengelola Kumpulan Test
Testing Model
Alternatif 1 - menggunakan data buatan atau training set yang sudah diketahui dan mudah untuk dihitung
return valuenya.
import pytest import numpy as np digunakan dataset yang
from models.train import train_model mengikuti persamaan
price = 2 * area
def test_on_linear_data(): + 1
test_argument = np.array([[1.0, 3.0],
[2.0, 5.0],
[3.0, 7.0] ] )
expected_slope = 2.0
expected_intercept = 1.0
slope, intercept = train_model(test_argument) Karena dataset mengikuti persamaan garis lurus,
assert slope == pytest.approx(expected_slope) maka fit linenya akan berupa garis lurus.
assert intercept == pytest.approx( Sehingga, nilai slope yang dihasilkan haruslah 2
expected_intercept dengan intercept 1. Ini bisa digunakan dalam
) assert statement untuk pengujian.

Python – Unit Testing > Mengelola Kumpulan Test > Testing Model
129
3. Mengelola Kumpulan Test
Testing Model
Alternatif 2 - menggunakan inequalities. Contohnya pada test test_on_positively_correlated_data(),
digunakan dataset yang berkorelasi positif
import numpy as np
from models.train import train_model

def test_on_positively_correlated_data():
test_argument = np.array([[1.0, 4.0], [2.0, 4.0],
[3.0, 9.0], [4.0, 10.0]
] )
slope, intercept = train_model(test_argument)
assert slope > 0
Dalam kondisi ini, kita tidak bisa memrediksi best
fit line, namun kita bisa melakukan assert bahwa
best fit line memiliki slope yang lebih besar dari 0.

Python – Unit Testing > Mengelola Kumpulan Test > Testing Model
130
3. Mengelola Kumpulan Test
Testing Model
Kita tidak boleh tidak menguji model hanya karena modelnya kompleks. Lakukan segala jenis pengujian,
karena hal ini akan menyelamatkan kita dari debugging yang lebih rumit di tahap selanjutnya.

Python – Unit Testing > Mengelola Kumpulan Test > Testing Model
131
3. Mengelola Kumpulan Test
Testing Model
from data.preprocessing_helpers Setelah training function sudah
import preprocess diuji, maka function sudah bisa
from features.as_numpy digunakan untuk menemukan best
import get_data_as_numpy_array fit line untuk housing data.
from models.train import split_into_training_and_testing_sets,
train_model
train_model(training_set)
preprocess("data/raw/housing_data.txt",
"data/clean/clean_housing_data.txt") 151.78430060614986
17140.77537937442
data = get_data_as_numpy_array(
"data/clean/clean_housing_data.txt", 2)
training_set, testing_set = (
split_into_training_and_testing_sets(data))
slope, intercept = train_model(training_set)

Python – Unit Testing > Mengelola Kumpulan Test > Testing Model
132
3. Mengelola Kumpulan Test
Testing Model
Langkah selanjutnya adalah menguji performa model menggunakan model_test() function. testing set
digunakan sebagai argumen pertama, kemudian slope dan intercept sebagai hasil yang akan dikeluarkan
oleh model, kemudian dilakukan uji performa pada data testing tersebut.

● Model mengeluarkan metrics r2 yang mengindikasikan kualitas


def model_test(testing_set,
performa model pada data baru
slope, intercept):
● Biasanya 0 ≤ r2 ≤ 1.
"""Return r^2 of fit"""
● r2 = 1 mengindikasikan fit sempurna
● r2= 0 mengindikasikan model tidak fit

Python – Unit Testing > Mengelola Kumpulan Test > Testing Model
133
3. Mengelola Kumpulan Test
Testing Plot
Test juga bisa dilakukan pada plot yang dibuat menggunakan matplotlib.
def get_plot_for_best_fit_line(slope,
matplotlib memiliki module plots.py. Modul ini mengandung
intercept, x_array, y_array, title ):
function get_plot_for_best_fit_line(). Modul ini menggunakan
"""
slope: slope of best fit line slope, intercept sebagai argumennya. Selain itu juga ada

intercept: intercept of best fit x_array dan y_array dari dataset, dan yang terakhir adalah
line x_array: array containing title.
housing areas y_array: array Function akan menghasilkan figure matplotlib.
containing housing prices title:
title of the plot

Returns: matplotlib.figure.Figure()
"""

Python – Unit Testing > Mengelola Kumpulan Test > Testing Model
134
3. Mengelola Kumpulan Test
Testing Plot
Setelah mendapatkan hasil dari train_model(), kita bisa gunakan function plotting seperti pada contoh
sehingga menghasilkan plot seperti pada gambar di bawah.
...

from visualization import get_plot_for_best_fit_line

preprocess(...)
data = get_data_as_numpy_array(...) training_set,
testing_set = (

split_into_training_and_testing_sets(data)

)
slope, intercept = train_model(training_set)
get_plot_for_best_fit_line(slope, intercept,
training_set[:, 0], training_set[:, 1],
"Training"

Python – Unit Testing > Mengelola Kumpulan Test > Testing Plot
135
3. Mengelola Kumpulan Test
Testing Plot
Testing strategy untuk plot mencakup dua cara, yaitu one-time baseline generation dan testing.

dalam one-time baseline generation, kita tentukan dalam test argument.


Kemudian kita panggil plotting function dengan test argument,
Mengkonversi object matplotlib.figure.Figure() ke dalam PNG,
Dilakukan inspect pada plot secara manual,
Cek apakah sudah sesuai atau belum. Jika sudah, maka plot disimpan sebagai
baseline image. Jika belum, maka dilakukan modifikasi plot.

Python – Unit Testing > Mengelola Kumpulan Test > Testing Plot
136
3. Mengelola Kumpulan Test
Testing Plot
Testing strategy untuk plot mencakup dua cara, yaitu one-time baseline generation dan testing.

step testing mencakup pembuatan PNG image dari test


arguments, kemudian dibandingkan dengan baseline
image.

Python – Unit Testing > Mengelola Kumpulan Test > Testing Plot
137
3. Mengelola Kumpulan Test
Testing Plot
Karena gambar yang dihasilkan dari os berbeda akan ditampilkan dengan sedikit berbeda juga, maka kita
perlu menggunakan pytest plugin pytest-mpl untuk komparasi image. Library ini mampu mengabaikan
perbedaan os dan memudahkan kita untuk memproduksi baseline image

pip install pytest-mpl

Python – Unit Testing > Mengelola Kumpulan Test > Testing Plot
138
3. Mengelola Kumpulan Test
Testing Plot
Perhatikan contoh berikut.

import pytest test_plot_for_linear_data().


import numpy as np
Test ini tidak mereturn assert
from visualization import get_plot_for_best_fit_line statement, melainkan matplotlib
figure berdasarkan function yang
@pytest.mark.mpl_image_compare ada di dalam test.
def test_plot_for_linear_data():
slope = 2.0
Itu karena kita menggunakan
intercept = 1.0
marker pytest.mark.mpl_image
x_array = np.array([1.0, 2.0, 3.0])
yang bertugas untuk melakukan
y_array = np.array([3.0, 5.0, 7.0])
image comparison dengan
title = "Test plot for linear data" baseline image
return get_plot_for_best_fit_line(slope, intercept, x_array, y_array, title)

Python – Unit Testing > Mengelola Kumpulan Test > Testing Plot
139
3. Mengelola Kumpulan Test
Testing Plot
pytest memerlukan baseline image untuk disimpan di folder ‘baseline’ relatif terhadap test modul
test_plots.py. Untuk membuat baseline image, kita bisa menjalankan test dengan command line argument
–mpl-generate-path dan tambahkan path ke folder yang diinginkan.
!pytest -k "test_plot_for_linear_data"
--mpl-generate-path
visualization/baseline

Kemudian, kita bisa lihat dan cek apakah baseline image


sudah sesuai ekspektasi.

Python – Unit Testing > Mengelola Kumpulan Test > Testing Plot
140
3. Mengelola Kumpulan Test
Testing Plot
Di lain waktu saat kita menjalankan test, kita harus menggunakan option –mpl dengan pytest command.
Hal ini akan membuat pytest meng-compare baseline image dengan gambar aktual. Jika gambar identik,
maka test akan berhasil.
!pytest -k "test_plot_for_linear_data" --mpl

Python – Unit Testing > Mengelola Kumpulan Test > Testing Plot
141
3. Mengelola Kumpulan Test
Testing Plot
Jika test gagal, maka pytest akan menyimpan baseline image, actual image, dan image yang berisi
perbedaan piksel antara keduanya ke dalam temporary directory.
!pytest -k "test_plot_for_linear_data" --mpl

Python – Unit Testing > Mengelola Kumpulan Test > Testing Plot
142
Summary
1. Testing merupakan kegiatan untuk menguji kesesuaian cara kerja function secara otomatis
2. Testing meningkatkan kepercayaan pengguna terhadap suatu function dengan proses pengujian yang
lebih cepat dan efektif.
3. Package pytest berguna untuk menguji hasil luaran function dan exceptions
4. Agar suatu function dikatakan well-tested, testing dilakukan pada tiga jenis argument, yaitu normal,
special, dan bad arguments
5. Test Driven Development merupakan best practice dalam testing di mana tests dituliskan sebelum
diimplementasi.
6. Beberapa function khusus perlu mengikuti skema setup - assert - teardown dalam pengujiannya.
7. Selain function, tests juga dilakukan pada model dan plot.

Python – Unit Testing > Summary


143
Glossary
1. Module atau library file berisikan sekumpulan kode fungsi dan variabel yang dapat digunakan berulang kali
dalam berbagai script.
2. Package sekumpulan modul
3. Function blok kode terorganisir dan dapat digunakan kembali untuk melakukan suatu tindakan/action tertentu
4. Parameter sebutan untuk nilai input pada fungsi python saat fungsi tersebut didefinisikan
5. Argument sebutan untuk nilai input pada fungsi python saat fungsi tersebut dipanggil
6. Schemas library untuk memvalidasi struktur data.
7. Raw Data (data mentah) sebuah kondisi untuk sebuah data di dalam sebuah sistem yang dikoleksi langsung
dari sebuah sumber langsung tanpa perubahan apapun.
8. Clean Data data yang sudah melalui preprocessing dan siap digunakan sebagai input model
9. DataFrame struktur data 2 dimensi seperti tabel yang berisi baris dan kolom.
10. Bugs error pada function yang menyebabkan function tidak bisa berjalan dengan semestinya.
11. Downtime kondisi di mana suatu sistem tidak dapat beroperasi sama sekali
12. Git Commit penyimpanan perubahan yang dilakukan, namun tidak ada perubahan yang terjadi pada remote
repository
13. Git Push mengirimkan perubahan file yang dilakukan setelah dicommit ke remote repository

Python – Unit Testing > Glossary


144
References
1. Chakravorty, Dibya : Unit Testing for Data Science in Python, viewed 20 Desember 2022, <
https://app.datacamp.com/learn/courses/unit-testing-for-data-science-in-python >

Python – Unit Testing > References


145
Pre & Post Quiz 1/5
1. __________ adalah kondisi error yang menyembunyikan kondisi error lainnya.
a. Fault Masking
b. Error Hiding
c. Redundant Masking
d. Fault Gaping

2. Untuk mendapatkan output test yang sesuai, dilakukan sebuah prosedur standar yang disebut ______
e. Testing Mechanism
f. Testing Type
g. Test Cast
h. Test Scope

Python – Unit Testing > Pre & Post Quiz 1/5


146
Pre & Post Quiz 2/5
3. Test meng-instantiates _____ dan memanggil methods pada _____ tersebut.
a. Objects
b. Classes
c. Services
d. Subsystems

4. Aplikasi berlapis bisa memiliki front end untuk mengatasi presentasi dan _______ untuk mengeksekusi
logika bisnis.
e. a) Objects
f. b) Classes
g. c) Servlet
h. d) Back end

Python – Unit Testing > Pre & Post Quiz 2/5


147
Pre & Post Quiz 2/4
3/5
5. ______________ tests menguji batasan kode pada public API-nya..
a. Unit
b. Integration
c. Functional
d. Loss

6. Developers seringkali menggabungkan functional test dengan _______ test.


e. Unit
f. Integration
g. Stress
h. Loss

Python – Unit Testing > Pre & Post Quiz 3/5


148
Pre & Post Quiz 3/4
4/5
7. ____________ tests menguji apakah aplikasi mampu memproses requests dalam jumlah yang banyak
dalam kurun waktu tertentu.
a. Unit
b. Integration
c. Stress
d. Acceptance

8. Stress test environment harus semirip mungkin dengan ________ environment.


e. Design
f. Review
g. Test
h. Production

Python – Unit Testing > Pre & Post Quiz 4/5


149
Pre & Post Quiz 5/5
9. Apa saja perbedaan tingkatan dari testing?
a. Integration testing
b. Unit testing
c. System testing
d. Semuanya benar

10. Pada environment apakah kita bisa melakukan alpha testing?


e. User's end
f. Developer's end
g. User's and developer's end
h. Semuanya salah

Python – Unit Testing > Pre & Post Quiz 5/5


150
Exam/Assessment/Assignment
Gunakan fixture untuk file clean data.

get_data_as_numpy_array() menggunakan path untuk ke file clean data sebagai argument pertama, dan jumlah
kolom pada data sebagai argument kedua. Function akan menghasilkan data dalam NumPy array. Sebelumnya, kita
telah menulis test test_on_clean_file() tanpa menggunakan fixture. Itu merupakan bad practice. Sekarang, gunakan
fixture clean_data_file() yang mampu:

○ Membuat clean data file di setup

○ Menghasilkan path menuju clean data file

○ Menghapus clean data file di teardown

Python – Unit Testing > Exam / Assessment / Assignment


151
Instructions
1. Tambahkan decorator yang sesuai untuk mengubah clean_data_File() menjadi sebuah fixture.
2. Gunakan argument ke dalam test_on_clean_file()
3. Gunakan clean data file path yang dihasilkan oleh future sebagai argumen pertama dalam function
get_data_as_numpy_array()

Python – Unit Testing > Instructions


152
Question
# Add a decorator to make this function a fixture
____
def clean_data_file():
    file_path = "clean_data_file.txt"
    with open(file_path, "w") as f:
        f.write("201\t305671\n7892\t298140\n501\t738293\n")
    yield file_path
    os.remove(file_path)
    
# Pass the correct argument so that the test can use the fixture
def test_on_clean_file(____):
    expected = np.array([[201.0, 305671.0], [7892.0, 298140.0], [501.0, 738293.0]])
    # Pass the clean data file path yielded by the fixture as the first argument
    actual = get_data_as_numpy_array(____, 2)
    assert actual == pytest.approx(expected), "Expected: {0}, Actual: {1}".format(expected, actual) 

Python – Unit Testing > Questions


153
Answer
# Add a decorator to make this function a fixture
@pytest.fixture
def clean_data_file():
    file_path = "clean_data_file.txt"
    with open(file_path, "w") as f:
        f.write("201\t305671\n7892\t298140\n501\t738293\n")
    yield file_path
    os.remove(file_path)
    
# Pass the correct argument so that the test can use the fixture
def test_on_clean_file(clean_data_file):
    expected = np.array([[201.0, 305671.0], [7892.0, 298140.0], [501.0, 738293.0]])
    # Pass the clean data file path yielded by the fixture as the first argument
    actual = get_data_as_numpy_array(clean_data_file, 2)
    assert actual == pytest.approx(expected), "Expected: {0}, Actual: {1}".format(expected, actual)

Python – Unit Testing > Answer


154
TEACHING NOTES 1/3
√ Lesson/Topics Case Study Clips Games Others : . . . . . . .
.
Objective Persiapan

Mampu memahami unit testing, assert, data set, Offline: Ruangan


mocking
Online: Menyiapkan link zoom

Overview Direction

Pada modul ini, user akan mendapatkan penjelasan 1. Menjelaskan apa, bagaimana dan tujuan dari
tentang Testing untuk data science, di modul ini juga kegiatan;
terdapat penjelasan unit testing, assert, data set, 2. Mengatur dan mengelola arah dan jalannya
mocking diskusi;
3. Memberikan contoh-contoh, kasus dan ilustrasi;
Peralatan yang dibutuhkan 4. Memberikan apresiasi dalam tanya jawab;
5. Memberikan debrief atau insight/pesan dari
kegiatan;
Offline: Proyektor 6. Memberikan dan menyampaikan kesimpulan dan
Online: Link zoom, laptop di tiap sesi atau di akhir sesi.

Python – Unit Testing > Teaching Notes 1/2


155
TEACHING NOTES 2/3
√ Lesson/Topics Case Study Clips Games Others : . . . . . . . .

To Do & Resources Key Activities/ Issues


Section (Hal apa yang akan dilakukan (hal-hal spesifik yang harus dilakukan, misal : penjelasan dan atau penekanan
(slide/clip/case/etc)
untuk setiap section-nya) materi, pengelolaan arah dan jalannya diskusi, wrap up/insight atau debrief, etc)

4 sd 5 Presentasi SME menjelaskan gambaran modul dan objektif dari pembelajaran.


6 sd 28 Presentasi, Diskusi SME menjelaskan kenapa unit test
29 sd 47 Presentasi, Diskusi SME menjelaskan simple unit testing
48 sd 60 Presentasi, Diskusi SME menjelaskan memahami test result report
61 sd 86 Presentasi, Diskusi SME menjelaskan tentang test types
87 sd 100 Presentasi, Diskusi SME menjelaskan assert
101 sd 113 Presentasi, Diskusi SME menjelaskan Testing for exceptions
114 sd 138 Presentasi, Diskusi SME menjelaskan well tested function
139 sd 149 Presentasi, Diskusi SME menjelaskan Test Driven Development (TDD)

Python – Unit Testing > Teaching Notes 2/2


156
TEACHING NOTES 3/3
√ Lesson/Topics Case Study Clips Games Others : . . . . . . . .

To Do & Resources Key Activities/ Issues


Section (Hal apa yang akan dilakukan (hal-hal spesifik yang harus dilakukan, misal : penjelasan dan atau penekanan
(slide/clip/case/etc)
untuk setiap section-nya) materi, pengelolaan arah dan jalannya diskusi, wrap up/insight atau debrief, etc)

150 sd 178 Presentasi, Diskusi SME menjelaskan data set


179 sd 199 Presentasi, Diskusi SME menjelaskan test execution
200 sd 224 Presentasi, Diskusi SME menjelaskan Expected failures & conditional skipping
225 sd 255 Presentasi, Diskusi SME menjelaskan Continuous integration & code coverage
256 sd 275 Presentasi, Diskusi SME menjelaskan setup and teardown
276 sd 294 Presentasi, Diskusi SME menjelaskan Mocking
295 sd 314 Presentasi, Diskusi SME menjelaskan Testing models
315 sd 352 Presentasi, Diskusi SME menjelaskan Testing plots

Python – Unit Testing > Teaching Notes 2/2


157
TERIMA KASIH

Last Update: 24 Desember 2022

Anda mungkin juga menyukai