有了 Jupyter、PyHamcrest,用一點測試的程式碼把它們連在一起,你就可以教任何適用於單元測試的 Python 內容。
Python影片教學專欄為大家詳細介紹~
# #關於Ruby 社群的一些事情一直讓我印象深刻,其中兩個例子是對測試的承諾和對易於上手的強調。這兩方面最好的例子是 Ruby Koans,在這裡你可以透過修復測試來學習 Ruby。 要是我們能把這些神奇的工具也用在 Python,我們應該可以做得更好。是的,使用 Jupyter Notebook、PyHamcrest,再加上一點類似於膠帶的黏合程式碼,我們可以做出一個包括教學、可工作的程式碼和需要修復的程式碼的教學。 首先,需要一些「膠布」。通常,你會使用一些漂亮的命令列測試器來做測試,例如 pytest 或 virtue。通常,你甚至不會直接運行它。你使用像 tox 或 nox 這樣的工具來運行它。然而,對於 Jupyter 來說,你需要寫一小段黏合程式碼,可以直接在其中執行測試。 幸運的是,這個程式碼又短又簡單:import unittest def run_test(klass): suite = unittest.TestLoader().loadTestsFromTestCase(klass) unittest.TextTestRunner(verbosity=2).run(suite) return klass复制代码現在,裝備已經就緒,可以進行第一次練習了。 在教學中,從一個簡單的練習開始,建立信心總是一個好主意。 那麼,讓我們來修復一個非常簡單的測驗:
@run_test class TestNumbers(unittest.TestCase): def test_equality(self): expected_value = 3 # 只改这一行 self.assertEqual(1+1, expected_value)复制代码
test_equality (__main__.TestNumbers) ... FAIL ====================================================================== FAIL: test_equality (__main__.TestNumbers) ---------------------------------------------------------------------- Traceback (most recent call last): File "<ipython-input-7-5ebe25bc00f3>", line 6, in test_equality self.assertEqual(1+1, expected_value) AssertionError: 2 != 3 ---------------------------------------------------------------------- Ran 1 test in 0.002s FAILED (failures=1)复制代码「只改變這一行」 對學生來說是一個有用的標記。它準確地表明了需要修改的內容。否則,學生可以透過將第一行改為
return 來修復測驗。
@run_test class TestNumbers(unittest.TestCase): def test_equality(self): expected_value = 2 # 修复后的代码行 self.assertEqual(1+1, expected_value)复制代码
test_equality (__main__.TestNumbers) ... ok ---------------------------------------------------------------------- Ran 1 test in 0.002s OK复制代码然而,很快,
unittest 庫的原生斷言將被證明是不夠的。在
pytest 中,透過重寫
assert 中的字節碼來解決這個問題,使其具有神奇的屬性和各種啟發式方法。但這在 Jupyter notebook 中就不容易實現了。是時候挖出一個好的斷言庫了:PyHamcrest。
from hamcrest import * @run_test class TestList(unittest.TestCase): def test_equality(self): things = [1, 5, # 只改这一行 3] assert_that(things, has_items(1, 2, 3))复制代码
test_equality (__main__.TestList) ... FAIL ====================================================================== FAIL: test_equality (__main__.TestList) ---------------------------------------------------------------------- Traceback (most recent call last): File "<ipython-input-11-96c91225ee7d>", line 8, in test_equality assert_that(things, has_items(1, 2, 3)) AssertionError: Expected: (a sequence containing <1> and a sequence containing <2> and a sequence containing <3>) but: a sequence containing <2> was <[1, 5, 3]> ---------------------------------------------------------------------- Ran 1 test in 0.004s FAILED (failures=1)复制代码PyHamcrest 不僅擅長靈活的斷言,它還擅長清晰的錯誤訊息。正因為如此,問題就顯而易見了。
[1, 5, 3] 不包含
2,而且看起來很醜:
@run_test class TestList(unittest.TestCase): def test_equality(self): things = [1, 2, # 改完的行 3] assert_that(things, has_items(1, 2, 3))复制代码
test_equality (__main__.TestList) ... ok ---------------------------------------------------------------------- Ran 1 test in 0.001s OK复制代码使用Jupyter、PyHamcrest 和一點測試的黏合程式碼,你可以教授任何適用於單元測試的Python 主題。 例如,下面可以幫助展示 Python 從字串中去掉空白的不同方法之間的差異。
source_string = " hello world " @run_test class TestList(unittest.TestCase): # 这是个赠品:它可以工作! def test_complete_strip(self): result = source_string.strip() assert_that(result, all_of(starts_with("hello"), ends_with("world"))) def test_start_strip(self): result = source_string # 只改这一行 assert_that(result, all_of(starts_with("hello"), ends_with("world "))) def test_end_strip(self): result = source_string # 只改这一行 assert_that(result, all_of(starts_with(" hello"), ends_with("world")))复制代码
test_complete_strip (__main__.TestList) ... ok test_end_strip (__main__.TestList) ... FAIL test_start_strip (__main__.TestList) ... FAIL ====================================================================== FAIL: test_end_strip (__main__.TestList) ---------------------------------------------------------------------- Traceback (most recent call last): File "<ipython-input-16-3db7465bd5bf>", line 19, in test_end_strip assert_that(result, AssertionError: Expected: (a string starting with ' hello' and a string ending with 'world') but: a string ending with 'world' was ' hello world ' ====================================================================== FAIL: test_start_strip (__main__.TestList) ---------------------------------------------------------------------- Traceback (most recent call last): File "<ipython-input-16-3db7465bd5bf>", line 14, in test_start_strip assert_that(result, AssertionError: Expected: (a string starting with 'hello' and a string ending with 'world ') but: a string starting with 'hello' was ' hello world ' ---------------------------------------------------------------------- Ran 3 tests in 0.006s FAILED (failures=2)复制代码理想情況下,學生會意識到
.lstrip() 和
.rstrip() 這兩個方法可以滿足他們的需要。但如果他們不這樣做,而是試圖到處使用
.strip() 的話:
source_string = " hello world " @run_test class TestList(unittest.TestCase): # 这是个赠品:它可以工作! def test_complete_strip(self): result = source_string.strip() assert_that(result, all_of(starts_with("hello"), ends_with("world"))) def test_start_strip(self): result = source_string.strip() # 改完的行 assert_that(result, all_of(starts_with("hello"), ends_with("world "))) def test_end_strip(self): result = source_string.strip() # 改完的行 assert_that(result, all_of(starts_with(" hello"), ends_with("world")))复制代码
test_complete_strip (__main__.TestList) ... ok test_end_strip (__main__.TestList) ... FAIL test_start_strip (__main__.TestList) ... FAIL ====================================================================== FAIL: test_end_strip (__main__.TestList) ---------------------------------------------------------------------- Traceback (most recent call last): File "<ipython-input-17-6f9cfa1a997f>", line 19, in test_end_strip assert_that(result, AssertionError: Expected: (a string starting with ' hello' and a string ending with 'world') but: a string starting with ' hello' was 'hello world' ====================================================================== FAIL: test_start_strip (__main__.TestList) ---------------------------------------------------------------------- Traceback (most recent call last): File "<ipython-input-17-6f9cfa1a997f>", line 14, in test_start_strip assert_that(result, AssertionError: Expected: (a string starting with 'hello' and a string ending with 'world ') but: a string ending with 'world ' was 'hello world' ---------------------------------------------------------------------- Ran 3 tests in 0.007s FAILED (failures=2)复制代码他們會得到一個不同的錯誤訊息,顯示去除了過多的空白:
source_string = " hello world " @run_test class TestList(unittest.TestCase): # 这是个赠品:它可以工作! def test_complete_strip(self): result = source_string.strip() assert_that(result, all_of(starts_with("hello"), ends_with("world"))) def test_start_strip(self): result = source_string.lstrip() # Fixed this line assert_that(result, all_of(starts_with("hello"), ends_with("world "))) def test_end_strip(self): result = source_string.rstrip() # Fixed this line assert_that(result, all_of(starts_with(" hello"), ends_with("world")))复制代码
test_complete_strip (__main__.TestList) ... ok test_end_strip (__main__.TestList) ... ok test_start_strip (__main__.TestList) ... ok ---------------------------------------------------------------------- Ran 3 tests in 0.005s OK复制代码在一個比較真實的教程中,會有更多的例子和更多的解釋。這種使用 Jupyter Notebook 的技巧,有的例子可以用,有的例子需要修正,可以用於實時教學,可以用於視頻課,甚至,可以用更多的其它零散用途,讓學生自己完成一個教程。 現在就去分享你的知識吧!
更多相關免費學習推薦:python影片教學
以上是使用Jupyter Notebook 學習 Python的詳細內容。更多資訊請關注PHP中文網其他相關文章!