Lihtne sissejuhatus Pythoniga testpõhiseks arenduseks

Olen iseõppinud alustav arendaja, kes suudab kirjutada lihtsaid äppe. Kuid mul on ülestunnistus teha. On võimatu meenutada, kuidas kõik on minu peas omavahel seotud.

See olukord muutub hullemaks, kui tulen mõne päeva pärast tagasi kirjutatud koodi juurde. Selgub, et sellest probleemist saaks üle saada, kui järgida testpõhise arenduse (TDD) metoodikat.

Mis on TDD ja miks see oluline on?

Võlakirjanduse mõistes soovitab TDD kirjutada teste, mis kontrolliksid teie koodi funktsionaalsust enne tegeliku koodi kirjutamist. Alles siis, kui olete oma testide ja testitavate funktsioonidega rahul, hakkate kirjutama tegelikku koodi, et täita testi seatud tingimusi, mis võimaldaksid neil läbida.

Selle protsessi järgimine tagab teie testide läbimiseks hoolikalt kirjutatava koodi planeerimise. See välistab ka testide kirjutamise võimaluse edasilükkamise, kuna neid ei pruugi pidada vajalikuks võrreldes selle aja jooksul loodavate lisafunktsioonidega.

Testid annavad teile enesekindlust ka siis, kui hakkate koodi refrakteerima, kuna testide läbiviimisel saate viivitamatu tagasiside tõttu tõenäolisemalt vigu.

Kuidas alustada?

Pythonis testide kirjutamise alustamiseks kasutame unittestPythoniga kaasas olevat moodulit. Selleks loome uue faili mytests.py, mis sisaldab kõiki meie teste.

Alustame tavalisest "tere maailmast":

import unittestfrom mycode import *
class MyFirstTests(unittest.TestCase):
def test_hello(self): self.assertEqual(hello_world(), 'hello world')

Pange tähele, et impordime helloworld()funktsiooni mycodefailist. Faili mycode.pylisame esialgu lihtsalt allpool oleva koodi, mis loob funktsiooni, kuid ei anna selles etapis midagi tagasi:

def hello_world(): pass

Käivitamine python mytests.pyloob käsureal järgmise väljundi:

F
====================================================================
FAIL: test_hello (__main__.MyFirstTests)
--------------------------------------------------------------------
Traceback (most recent call last):
File "mytests.py", line 7, in test_hello
self.assertEqual(hello_world(), 'hello world')
AssertionError: None != 'hello world'
--------------------------------------------------------------------
Ran 1 test in 0.000s
FAILED (failures=1)

See näitab selgelt, et test ebaõnnestus, mida oodati. Õnneks oleme testid juba kirjutanud, nii et teame, et selle funktsiooni kontrollimiseks on alati olemas, mis annab meile enesekindluse võimalike vigade tuvastamisel tulevikus.

Koodi läbimise tagamiseks laseb see muuta mycode.pyjärgmiseks:

def hello_world(): return 'hello world'

python mytests.pyUuesti käivitades saame käsureal järgmise väljundi:

.
--------------------------------------------------------------------
Ran 1 test in 0.000s
OK

Palju õnne! Olete just kirjutanud oma esimese testi. Liigume nüüd veidi keerulisema väljakutse juurde. Loome funktsiooni, mis võimaldaks meil luua Pythonis kohandatud numbriloendi mõistmise.

Alustame testi kirjutamisest funktsioonile, mis looks konkreetse pikkusega loendi.

Failis mytests.pyoleks see meetod test_custom_num_list:

import unittestfrom mycode import *
class MyFirstTests(unittest.TestCase):
def test_hello(self): self.assertEqual(hello_world(), 'hello world') def test_custom_num_list(self): self.assertEqual(len(create_num_list(10)), 10)

See test, et funktsioon create_num_listtagastab nimekirja pikkus 10. Loome funktsiooni create_num_listsisse mycode.py:

def hello_world(): return 'hello world'
def create_num_list(length): pass

Käivitamine python mytests.pyloob käsureal järgmise väljundi:

E.
====================================================================
ERROR: test_custom_num_list (__main__.MyFirstTests)
--------------------------------------------------------------------
Traceback (most recent call last):
File "mytests.py", line 14, in test_custom_num_list
self.assertEqual(len(create_num_list(10)), 10)
TypeError: object of type 'NoneType' has no len()
--------------------------------------------------------------------
Ran 2 tests in 0.000s
FAILED (errors=1)

See on ootuspäraselt, nii lähme edasi ja muutus funktsiooni create_num_listsisse mytest.py, et katse:

def hello_world(): return 'hello world'
def create_num_list(length): return [x for x in range(length)]

Käsureal python mytests.pykäivitamine näitab, et ka teine ​​test on nüüd läbitud:

..
--------------------------------------------------------------------
Ran 2 tests in 0.000s
OK

Let’s now create a custom function that would transform each value in the list like this: const * ( X ) ^ power . First let’s write the test for this, using method test_custom_func_ that would take value 3 as X, take it to the power of 3, and multiply by a constant of 2, resulting in the value 54:

import unittestfrom mycode import *
class MyFirstTests(unittest.TestCase):
def test_hello(self): self.assertEqual(hello_world(), 'hello world')
def test_custom_num_list(self): self.assertEqual(len(create_num_list(10)), 10) def test_custom_func_x(self): self.assertEqual(custom_func_x(3,2,3), 54)

Let’s create the function custom_func_x in the file mycode.py:

def hello_world(): return 'hello world'
def create_num_list(length): return [x for x in range(length)]
def custom_func_x(x, const, power): pass

As expected, we get a fail:

F..
====================================================================
FAIL: test_custom_func_x (__main__.MyFirstTests)
--------------------------------------------------------------------
Traceback (most recent call last):
File "mytests.py", line 17, in test_custom_func_x
self.assertEqual(custom_func_x(3,2,3), 54)
AssertionError: None != 54
--------------------------------------------------------------------
Ran 3 tests in 0.000s
FAILED (failures=1)

Updating function custom_func_x to pass the test, we have the following:

def hello_world(): return 'hello world'
def create_num_list(length): return [x for x in range(length)]
def custom_func_x(x, const, power): return const * (x) ** power

Running the tests again we get a pass:

...
--------------------------------------------------------------------
Ran 3 tests in 0.000s
OK

Finally, let’s create a new function that would incorporate custom_func_x function into the list comprehension. As usual, let’s begin by writing the test. Note that just to be certain, we include two different cases:

import unittestfrom mycode import *
class MyFirstTests(unittest.TestCase):
def test_hello(self): self.assertEqual(hello_world(), 'hello world')
def test_custom_num_list(self): self.assertEqual(len(create_num_list(10)), 10)
def test_custom_func_x(self): self.assertEqual(custom_func_x(3,2,3), 54)
def test_custom_non_lin_num_list(self): self.assertEqual(custom_non_lin_num_list(5,2,3)[2], 16) self.assertEqual(custom_non_lin_num_list(5,3,2)[4], 48)

Now let’s create the function custom_non_lin_num_list in mycode.py:

def hello_world(): return 'hello world'
def create_num_list(length): return [x for x in range(length)]
def custom_func_x(x, const, power): return const * (x) ** power
def custom_non_lin_num_list(length, const, power): pass

As before, we get a fail:

.E..
====================================================================
ERROR: test_custom_non_lin_num_list (__main__.MyFirstTests)
--------------------------------------------------------------------
Traceback (most recent call last):
File "mytests.py", line 20, in test_custom_non_lin_num_list
self.assertEqual(custom_non_lin_num_list(5,2,3)[2], 16)
TypeError: 'NoneType' object has no attribute '__getitem__'
--------------------------------------------------------------------
Ran 4 tests in 0.000s
FAILED (errors=1)

In order to pass the test, let’s update the mycode.py file to the following:

def hello_world(): return 'hello world'
def create_num_list(length): return [x for x in range(length)]
def custom_func_x(x, const, power): return const * (x) ** power
def custom_non_lin_num_list(length, const, power): return [custom_func_x(x, const, power) for x in range(length)]

Running the tests for the final time, we pass all of them!

....
--------------------------------------------------------------------
Ran 4 tests in 0.000s
OK

Congrats! This concludes this introduction to testing in Python. Make sure you check out the resources below for more information on testing in general.

The code is available here on GitHub.

Useful resources for further learning!

Web resources

Below are links to some of the libraries focusing on testing in Python

25.3. unittest - Unit testing framework - Python 2.7.14 documentation

The Python unit testing framework, sometimes referred to as "PyUnit," is a Python language version of JUnit, by Kent…docs.python.orgpytest: helps you write better programs - pytest documentation

Raamistik hõlbustab väikeste testide kirjutamist, kuid võimaldab ka rakenduste keerukat funktsionaalset testimist ja… docs.pytest.org Tere tulemast hüpoteesi! - hüpoteesi 3.45.2 dokumentatsioon

See töötab genereerides teie spetsifikatsioonidele vastavaid juhuslikke andmeid ja kontrollides, kas teie garantii kehtib ka selles ... hüpotees.readthedocs.io unittest2 1.1.0: Pythoni pakettide register

Uued funktsioonid unportestis on tagasi viidud Python 2.4+ versioonile. pypi.python.org

YouTube'i videod

Kui eelistate mitte lugeda, soovitan YouTube'is vaadata järgmisi videoid.