Skip to content

Commit

Permalink
chore: integrating doctests
Browse files Browse the repository at this point in the history
  • Loading branch information
aorumbayev committed Aug 19, 2024
1 parent f35d145 commit 1e71d5b
Show file tree
Hide file tree
Showing 12 changed files with 146 additions and 77 deletions.
4 changes: 0 additions & 4 deletions docs/_static/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,3 @@
.py.class {
margin-top: 3rem;
}

.jupyter_container .stderr {
background-color: #7fff00;
}
3 changes: 0 additions & 3 deletions docs/api-algopy-testing.md

This file was deleted.

15 changes: 10 additions & 5 deletions docs/api.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
# API Reference

```{toctree}
---
maxdepth: 4
---
```{autodoc2-summary}
:renderer: myst
api-algopy-testing
algopy_testing.AlgopyTestContext
algopy_testing.LedgerContext
algopy_testing.TransactionContext
algopy_testing.AVMValueGenerator
algopy_testing.TxnValueGenerator
algopy_testing.ARC4ValueGenerator
```

> TODO: 1.0 Restructure algopy_testing index file once refactoring changes are merged
21 changes: 12 additions & 9 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"sphinx_copybutton",
"myst_parser",
"autodoc2", # Add this line
"jupyter_sphinx",
"sphinx.ext.doctest",
]

templates_path = ["_templates"]
Expand Down Expand Up @@ -56,25 +56,28 @@
python_maximum_signature_line_length = 80

# -- Options for myst ---
myst_enable_extensions = [
"colon_fence",
"fieldlist",
]
myst_enable_extensions = ["colon_fence", "fieldlist"]

# Add autodoc2 configuration
autodoc2_packages = [
{
"path": "../src/algopy_testing",
"module": "algopy_testing",
"auto_mode": False,
"auto_mode": True,
},
]
autodoc2_render_plugin = "myst"
autodoc2_hidden_objects = [
"private", # single-underscore methods, e.g. _private
"dunder",
"private",
"undoc",
]
add_module_names = False
autodoc2_index_template = None

jupyter_sphinx_execute_kernelspec = "python3"
doctest_global_setup = """
import algopy
from algopy import arc4
import algopy_testing
from algopy_testing import AlgopyTestContext, algopy_testing_context
"""
doctest_test_doctest_blocks = "default"
33 changes: 22 additions & 11 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
# Algorand Python Testing

`algopy-testing` is a companion package to [Algorand Python](https://github.com/algorandfoundation/puya) that enables efficient unit testing of Algorand Python smart contracts in an offline environment. This package emulates key AVM behaviors without requiring a network connection, offering fast and reliable testing capabilities with a familiar Pythonic interface.
`algorand-python-testing` is a companion package to [Algorand Python](https://github.com/algorandfoundation/puya) that enables efficient unit testing of Algorand Python smart contracts in an offline environment. This package emulates key AVM behaviors without requiring a network connection, offering fast and reliable testing capabilities with a familiar Pythonic interface.

The `algopy-testing` package provides:
The `algorand-python-testing` package provides:

- A simple interface for fast and reliable unit testing
- An offline testing environment that simulates core AVM functionality
- A familiar Pythonic experience, compatible with testing frameworks like [pytest](https://docs.pytest.org/en/latest/), [unittest](https://docs.python.org/3/library/unittest.html), and [hypothesis](https://hypothesis.readthedocs.io/en/latest/)

## Quick Start

`algopy` is a prerequisite for `algopy-testing`, providing stubs and type annotations for Algorand Python syntax. It enhances code completion and type checking when writing smart contracts. Note that this code isn't directly executable in standard Python interpreters; it's compiled by `puya` into TEAL for Algorand Network deployment.
`algopy` is a prerequisite for `algorand-python-testing`, providing stubs and type annotations for Algorand Python syntax. It enhances code completion and type checking when writing smart contracts. Note that this code isn't directly executable in standard Python interpreters; it's compiled by `puya` into TEAL for Algorand Network deployment.

Traditionally, testing Algorand smart contracts involved deployment on sandboxed networks and interacting with live instances. While robust, this approach can be inefficient and lacks versatility for testing Algorand Python code.

Enter `algopy-testing`: it leverages Python's rich testing ecosystem for unit testing without network deployment. This enables rapid iteration and granular logic testing.
Enter `algorand-python-testing`: it leverages Python's rich testing ecosystem for unit testing without network deployment. This enables rapid iteration and granular logic testing.

> **NOTE**: While `algopy-testing` offers valuable unit testing capabilities, it's not a replacement for comprehensive testing. Use it alongside other test types, particularly those running against the actual Algorand Network, for thorough contract validation.
> **NOTE**: While `algorand-python-testing` offers valuable unit testing capabilities, it's not a replacement for comprehensive testing. Use it alongside other test types, particularly those running against the actual Algorand Network, for thorough contract validation.
### Prerequisites

Expand All @@ -25,7 +25,7 @@ Enter `algopy-testing`: it leverages Python's rich testing ecosystem for unit te

### Installation

`algopy-testing` is distributed via [PyPI](https://pypi.org/project/algopy-testing/). Install the package using `pip`:
`algorand-python-testing` is distributed via [PyPI](https://pypi.org/project/algorand-python-testing/). Install the package using `pip`:

```bash
pip install algorand-python-testing
Expand All @@ -39,11 +39,11 @@ poetry add algorand-python-testing

### Testing your first contract

Let's write a simple contract and test it using the `algopy-testing` framework.
Let's write a simple contract and test it using the `algorand-python-testing` framework.

#### Contract Definition

```{jupyter-execute}
```{testcode}
import algopy
from algopy import arc4
Expand Down Expand Up @@ -84,7 +84,7 @@ class VotingContract(algopy.ARC4Contract):

#### Test Definition

```{jupyter-execute}
```{testcode}
from collections.abc import Generator
import pytest
from algopy_testing import AlgopyTestContext, algopy_testing_context
Expand Down Expand Up @@ -120,7 +120,18 @@ with algopy_testing_context() as context:
print(f"Current votes: {votes.native}")
```

This interactive `jupyter` example demonstrates key aspects of testing with `algopy-testing` for ARC4-based contracts:
```{testoutput}
:hide:
Vote result: True
Total votes: 1
Voter ... voted: 1
New topic: ...
Contract topic: ...
Current votes: 5
```

This example demonstrates key aspects of testing with `algorand-python-testing` for ARC4-based contracts:

1. ARC4 Contract Features:

Expand Down Expand Up @@ -148,7 +159,7 @@ This interactive `jupyter` example demonstrates key aspects of testing with `alg
### Next steps

To dig deeper into the capabilities of `algopy-testing`, continue with the following sections.
To dig deeper into the capabilities of `algorand-python-testing`, continue with the following sections.

```{toctree}
---
Expand Down
20 changes: 16 additions & 4 deletions docs/testing-guide/avm-types.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,33 @@
# AVM Types

These types are available directly under the `algopy` namespace. They represent the basic AVM primitive types and can be instantiated as follows:
These types are available directly under the `algopy` namespace. They represent the basic AVM primitive types and can be instantiated directly or via _value generators_:

```{testsetup}
import algopy
import algopy_testing
ctx = algopy_testing.AlgopyTestContext()
```

## UInt64

```python
```{testcode}
# Direct instantiation
uint64_value = algopy.UInt64(100)
# Instantiate test context
...
# Generate a random UInt64 value
random_uint64 = ctx.any_uint64()
random_uint64 = ctx.any.uint64()
# Specify a range
random_uint64 = ctx.any_uint64(min_value=1000, max_value=9999)
random_uint64 = ctx.any.uint64(min_value=1000, max_value=9999)
print(random_uint64)
```

```{testoutput}
...
```

## Bytes
Expand Down
72 changes: 49 additions & 23 deletions docs/testing-guide/concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ The following sections provide an overview of the key concepts and features of t

## Test Context

The main abstraction to interact with the testing framework is the [`AlgopyTestContext`](algopy_testing.AlgopyTestContext). It creates an emulated Algorand environment that closely mimics AVM behavior relevant to unit testing the contracts and provides a Pythonic interface for interacting with the emulated environment.
The main abstraction to interact with the testing framework is the [`AlgopyTestContext`](../api-context.md#algopy_testing.AlgopyTestContext). It creates an emulated Algorand environment that closely mimics AVM behavior relevant to unit testing the contracts and provides a Pythonic interface for interacting with the emulated environment.

```python
from algopy_testing import algopy_testing_context
Expand All @@ -20,6 +20,14 @@ def test_my_contract():
ctx.reset() # Reset the emulated environment
```

In short, context manager exposes three main properties:

1. `.any` - A property returning an instance of AlgopyValueGenerator. This provides methods for generating randomized test data for various AVM types like accounts, assets, applications, transactions, ARC4 types, etc. It allows generating constrained random values when exact values are not needed.
2. `.ledger` - A property returning an instance of LedgerContext. This provides methods for interacting with and querying the emulated Algorand ledger state, including accounts, assets, applications, global state, etc.
3. `.txn` - A property returning an instance of TransactionContext. This provides methods for creating and managing transaction groups, submitting transactions, and accessing transaction results in the emulated environment.

Certainly! I'll provide a concise overview of the user-facing usage for the `LedgerContext` and `TransactionContext` classes without being redundant. For detailed method signatures and docstrings, users should refer to the auto-generated API documentation in `api-context.md`.

### Managing Test Context State

1. **Automatic Reset with Context Manager**:
Expand All @@ -32,41 +40,45 @@ def test_my_contract():

Recommended for its automatic management and efficiency.

2. **Manual Cleanup with `clear()`**:

```python
ctx = AlgopyTestContext()
... # your test code here
ctx.clear()
```

Clears state without resetting counters or recreating default objects. Useful between related tests.

3. **Manual Reset with `reset()`**:
2. **Manual Reset with `reset()`**:
```python
ctx = AlgopyTestContext()
... # your test code here
ctx.reset()
```
Thoroughly resets the context, reinitializing all data structures and settings. Ideal for unrelated test suites.

```{hint}
Use `clear()` for quick state clearance between related tests, and `reset()` for a completely fresh environment.
```
## Context properties

## Types of `algopy` stub implementations
These classes are part of the Algorand Python Testing framework and provide methods for interacting with the emulated Algorand environment.

As explained in the [introduction](index.md), `algorand-python-testing` _injects_ test implementations for stubs available in `algorand-python` package. However, not all of the stubs implemented in the same manner:
### 1. Ledger

1. **Native**: Fully matches AVM computation in Python. For example, `algopy.op.sha256` and other cryptographic operations behave identically in AVM and unit tests. This implies the majority of opcodes that are 'pure' functions in AVM also have a native Python implementation provided by this package. These abstractions and opcodes can be used within and outside of the testing context.
The `LedgerContext` allows you to interact with and query the emulated Algorand ledger state. Key operations include:

2. **Emulated**: Uses `AlgopyTestContext` to mimic AVM behavior. For example, `Box.put` on an `algopy.Box` within a test context stores data in the test manager, not the real Algorand network, but provides the same interface.
1. Account management (get, check existence, update)
2. Asset operations (get, check existence, update)
3. Application interactions (get, check existence, update)
4. State management (global and local)
5. Box operations (get, set, delete, check existence)
6. Block operations (set, get content)
7. `algopy.Global` fields patching

3. **Mockable**: Not implemented but can be mocked or patched. For example, `algopy.abi_call` can be mocked to return specific values or behaviors; otherwise, it raises a `NotImplementedError`. In other words, this category covers the cases where native or emulated implementation in a unit test context is impractical or overly complex.
> Refer to the [`algopy_testing.LedgerContext`](../api.md) in the API section for detailed method signatures, parameters and return types.

For a full list of all public `algopy` types and their corresponding implementation category, refer to the [Coverage](coverage.md) section.
### 2. Transactions

## Value generators
The `TransactionContext` enables creation and management of transaction groups, submission of transactions, and access to transaction results. Key features include:

1. Transaction group management
2. Deferred application calls
3. Inner transaction handling
4. Access to individual transactions
5. Scratch space operations

> Refer to the [`algopy_testing.TransactionContext`](../api.md) for detailed method signatures, parameters and return types.

### 3. Value generators

Testing context provides an range of helper methods called _value generators_ which allow quick generate and/or instantiation of randomized values for specified AVM types, which is also a common building block in _property-based_ testing methodologies. To access them, refer to methods prefixed with word `any_*` or `arc4.any_*` on the test context instance.

Expand All @@ -78,6 +90,20 @@ Value generators are a powerful tool for generating test data for specified AVM
If used with the 'Arrange, Act, Assert' pattern, value generators can be especially useful in setting up clear and concise test data in arrange steps.
```

### Property-based testing
> Refer to the [`algopy_testing.AVMValueGenerator`, `algopy_testing.TxnValueGenerator`, `algopy_testing.ARC4ValueGenerator`](../api.md) for detailed method signatures, parameters and return types.

#### Property-based testing

`algorand-python-testing` aims to be agnostic of the specific Python testing framework being used. The [`value generators`](#value-generators), serve as a base building block that can be integrated/reused with popular Python property-based testing frameworks like [`hypothesis`](https://hypothesis.readthedocs.io/en/latest/).

## Types of `algopy` stub implementations

As explained in the [introduction](index.md), `algorand-python-testing` _injects_ test implementations for stubs available in `algorand-python` package. However, not all of the stubs implemented in the same manner:

1. **Native**: Fully matches AVM computation in Python. For example, `algopy.op.sha256` and other cryptographic operations behave identically in AVM and unit tests. This implies the majority of opcodes that are 'pure' functions in AVM also have a native Python implementation provided by this package. These abstractions and opcodes can be used within and outside of the testing context.

2. **Emulated**: Uses `AlgopyTestContext` to mimic AVM behavior. For example, `Box.put` on an `algopy.Box` within a test context stores data in the test manager, not the real Algorand network, but provides the same interface.

3. **Mockable**: Not implemented but can be mocked or patched. For example, `algopy.abi_call` can be mocked to return specific values or behaviors; otherwise, it raises a `NotImplementedError`. In other words, this category covers the cases where native or emulated implementation in a unit test context is impractical or overly complex.

For a full list of all public `algopy` types and their corresponding implementation category, refer to the [Coverage](coverage.md) section.
2 changes: 1 addition & 1 deletion docs/testing-guide/contract-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This guide provides an overview of how to test smart contracts using the Algorand Python SDK (`algopy`). We will cover the basics of testing `ARC4Contract` and `Contract` classes, focusing on `abimethod` and `baremethod` decorators.

[![](https://mermaid.ink/img/pako:eNqVkrFugzAQhl_Fujnp1ImhEiJrJNREWeoOV9sNVsFG9iEVBd69R5w0JE2llsk2n7-7_-AAymsDGewDtpXYrqQT_GyKFwl5vfcBnRZlT5V3IjYYSCjvKKAiCa-JzXfrObyzgTqsxRpVZZ25YOX2nnRrIomCneZzpszLkllktu0f8ratrUKyjFsXCZ1K2gTH7i01_8dGUjOT_55YeLdUFVr3zRunf5b6R5hZoFnBq9cX72_Br_Cj8bl4vJCHaVucvowYxHk5Xg_sfPkY6SbbphDL5dMgQZu29n0U5DMJwzTVGyApySKZKFSNMXKVxPJYYAGNCQ1azX_VYboqgSrTcAcZLzWGDwnSjcxhR37TOwUZhc4sIPhuX0H2jnXkXddqrrCyyKNpTqfjF5m74B8?type=png)](https://mermaid.live/edit#pako:eNqVkrFugzAQhl_Fujnp1ImhEiJrJNREWeoOV9sNVsFG9iEVBd69R5w0JE2llsk2n7-7_-AAymsDGewDtpXYrqQT_GyKFwl5vfcBnRZlT5V3IjYYSCjvKKAiCa-JzXfrObyzgTqsxRpVZZ25YOX2nnRrIomCneZzpszLkllktu0f8ratrUKyjFsXCZ1K2gTH7i01_8dGUjOT_55YeLdUFVr3zRunf5b6R5hZoFnBq9cX72_Br_Cj8bl4vJCHaVucvowYxHk5Xg_sfPkY6SbbphDL5dMgQZu29n0U5DMJwzTVGyApySKZKFSNMXKVxPJYYAGNCQ1azX_VYboqgSrTcAcZLzWGDwnSjcxhR37TOwUZhc4sIPhuX0H2jnXkXddqrrCyyKNpTqfjF5m74B8)
![](https://mermaid.ink/img/pako:eNqVkrFugzAQhl_Fujnp1ImhEiJrJNREWeoOV9sNVsFG9iEVBd69R5w0JE2llsk2n7-7_-AAymsDGewDtpXYrqQT_GyKFwl5vfcBnRZlT5V3IjYYSCjvKKAiCa-JzXfrObyzgTqsxRpVZZ25YOX2nnRrIomCneZzpszLkllktu0f8ratrUKyjFsXCZ1K2gTH7i01_8dGUjOT_55YeLdUFVr3zRunf5b6R5hZoFnBq9cX72_Br_Cj8bl4vJCHaVucvowYxHk5Xg_sfPkY6SbbphDL5dMgQZu29n0U5DMJwzTVGyApySKZKFSNMXKVxPJYYAGNCQ1azX_VYboqgSrTcAcZLzWGDwnSjcxhR37TOwUZhc4sIPhuX0H2jnXkXddqrrCyyKNpTqfjF5m74B8?type=png)

```{note}
The code snippets showcasing the contract testing capabilities are using [pytest](https://docs.pytest.org/en/latest/) as the test framework. However, note that the `algorand-python-testing` package can be used with any other test framework that supports Python. `pytest` is used for demonstration purposes in this documentation.
Expand Down
Loading

0 comments on commit 1e71d5b

Please sign in to comment.