Słowem wstępu...
W poniższym artykule, przedstawię testy dla kodu stworzonego w ramach serii artykułów "Budujemy własny Blockchain cz 1" oraz "Budujemy własny Blockchain cz 2". Pełny kod w formie paczki python znajduje się pod poniższym adresem: https://github.com/Kamwebdev/Blockchain
Stosując uzgodnioną na początku konwencję, po krótkim wprowadzeniu, zachęcam do przeanalizowania kodu, dokopania się co oznacza każda z linijek a gwarantuję, czeka Cię wspaniała przygoda!
Wprowadzenie...
Na samym początku naszych testów importujemy nasz blockchain i wymagane paczki.
import os
import mock
import pytest
from blockchain import Blockchain
Po czym tworzymy nasz pierwszy test sprawdzający czy udało się nam stworzyć pierwszy blok:
def test_blockchain_create_first_block():
my_blockchain = Blockchain(difficulty=3)
assert my_blockchain.last_block.previous_hash == "0"
Aby go uruchomić testy, używamy z paczki pytests modułu, który uruchamiamy w poniższy sposób:
pytests .
*Co ciekawe, możemy również zaczerpnąć z techniki TDD pewnien smaczek i uruchomić watcher (link) który przy każdym zapisie, na nowo odpali testy.
pytest-watch
Jak widzimy test się wykonał, więc pierwsze przełamanie lodów za nami. Teraz przejdziemy do części właściwej (wyzwania).
Wyzwanie:
Spróbuj teraz drogi czytelniku sam przeanalizować poniższy kod:
conftest.py
import pytest
from blockchain import Blockchain
def pytest_runtest_setup():
print("Start test")
@pytest.fixture()
def backend(tmpdir):
temp_file = tmpdir.join("test.txt")
temp_file.write("")
return temp_file
@pytest.fixture(params=[None, "backend"], name="my_blockchain")
def fixture_my_blockchain(backend, request):
if request.param == "backend":
my_blockchain = Blockchain(backend=backend)
else:
my_blockchain = Blockchain()
yield my_blockchain
assert my_blockchain.verify_full_block_history()
@pytest.fixture(name="my_filled_blockchain")
def fixture_my_filled_blockchain(my_blockchain, request):
my_blockchain.add_new_transaction("test")
my_blockchain.mine()
yield my_blockchain
assert my_blockchain.verify_full_block_history()
blockchain_test.py
import os
import mock
import pytest
from blockchain import Blockchain
def test_blockchain_initialization(my_blockchain):
assert my_blockchain
def test_blockchain_create_first_block():
my_blockchain = Blockchain(difficulty=3)
assert my_blockchain.last_block.previous_hash == "0"
@pytest.mark.parametrize(
"message",
(
"test",
"123",
"0" * 50,
),
)
def test_blockchain_mine_first_block(my_blockchain, message):
my_blockchain.add_new_transaction(message)
my_blockchain.mine()
assert message in my_blockchain.last_block.transactions
def test_blockchain_save_backend_empty():
my_blockchain = Blockchain()
with pytest.raises(Exception):
my_blockchain.save_transactions("test")
def test_blockchain_read_backend_empty():
my_blockchain = Blockchain()
with pytest.raises(Exception):
my_blockchain.read_transactions()
def test_blockchain_read_backend_unconfirmed_transactions(backend):
my_blockchain = Blockchain(backend=backend)
my_blockchain.save_transactions("test")
my_blockchain.read_transactions()
my_blockchain.mine()
assert "test" in my_blockchain.last_block.transactions
def test_blockchain_bad_proof_of_work(mocker, my_filled_blockchain):
mocker.patch.object(Blockchain, "proof_of_work")
my_filled_blockchain.proof_of_work.return_value = "01"
my_filled_blockchain.add_new_transaction("second")
my_filled_blockchain.mine()
assert "second" not in my_filled_blockchain.last_block.transactions
def test_blockchain_bad_previus_hash(my_blockchain):
my_blockchain.add_new_transaction("test")
my_blockchain.mine()
my_blockchain.add_new_transaction("test2")
my_blockchain.mine()
with pytest.raises(AttributeError):
my_blockchain.last_block.previous_hash = 32 * "01"
def test_blockchain_set_block_hash(my_filled_blockchain):
assert "test" in my_filled_blockchain.last_block()
assert my_filled_blockchain.last_block.hash
def test_invalid_block_hash(my_filled_blockchain):
with pytest.raises(ValueError):
my_filled_blockchain.last_block.hash = "invalid"
with pytest.raises(TypeError):
my_filled_blockchain.last_block.hash = 123
assert "test" in my_filled_blockchain.last_block()
@pytest.mark.skipif(
os.environ.get("SKIP_ASYNC_TESTS") == "1",
reason="Flag SKIP_ASYNC_TESTS is set.",
)
def test_initialize_two_blockchain_classes(backend):
my_blockchain1 = Blockchain(backend=backend)
my_blockchain2 = Blockchain(backend=backend)
my_blockchain1.add_new_transaction("test")
my_blockchain2.mine()
my_blockchain1.mine()
assert "test" in my_blockchain2.last_block.transactions
assert "test" not in my_blockchain1.last_block.transactions
my_blockchain2.get_full_block()
def test_valid_proof(my_blockchain):
my_blockchain.is_valid_proof = mock.Mock()
my_blockchain.is_valid_proof.return_value = 1
assert my_blockchain.is_valid_proof() == 1
def test_last_block_return_value(mocker, my_blockchain):
mocker.patch.object(Blockchain, "last_block")
my_blockchain.last_block.return_value = 1
assert my_blockchain.last_block() == 1
Celem zrozumienia go :)
Bonusowe zadanie:
Narzędzie pytest, daje nam możliwość sprawdzenia naszego kodu, pod względem pokrycia go.
pytest --cov blockchain
Po wykonaniu powyższego kodu, możemy się przekonać że nasze testy pokryły 98% naszego kodu.
Spróbuj znaleźć brakujące 2% :)
Komentarze