Skip to content

Commit

Permalink
Fix "Testing Smart Contracts" code (#2327)
Browse files Browse the repository at this point in the history
<!-- Reference any GitHub issues resolved by this PR --

## Introduced changes

<!-- A brief description of the changes -->

- After testing this section some code snippets and explanations needed
to be updated

## Checklist

<!-- Make sure all of these are complete -->

- [x] Linked relevant issue
- [x] Updated relevant documentation
- [x] Added relevant tests
- [x] Performed self-review of the code
- [x] Added changes to `CHANGELOG.md`
  • Loading branch information
Arcticae authored Jul 19, 2024
1 parent 47d52bf commit 4f3e24f
Showing 1 changed file with 24 additions and 20 deletions.
44 changes: 24 additions & 20 deletions docs/src/testing/contracts.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Testing Smart Contracts

> ℹ️ **Info**
>
> To use the library functions designed for testing smart contracts,
> you need to add `snforge_std` package as a dependency in
> your [`Scarb.toml`](https://docs.swmansion.com/scarb/docs/guides/dependencies.html#development-dependencies)
Expand All @@ -15,11 +16,11 @@ writing smart contracts, you often want to test their interactions with the bloc
## The Test Contract
In this tutorial we will be using this Starknet contract
In this tutorial we will be using this Starknet contract, in a new `using_dispatchers` package.
```rust
#[starknet::interface]
trait IHelloStarknet<TContractState> {
pub trait IHelloStarknet<TContractState> {
fn increase_balance(ref self: TContractState, amount: felt252);
fn get_balance(self: @TContractState) -> felt252;
}
Expand Down Expand Up @@ -54,6 +55,7 @@ Let's write a test that will deploy the `HelloStarknet` contract and call some f

```rust
use snforge_std::{ declare, ContractClassTrait };
use using_dispatchers::{ IHelloStarknetDispatcher, IHelloStarknetDispatcherTrait };

#[test]
fn call_and_invoke() {
Expand Down Expand Up @@ -122,14 +124,12 @@ mod HelloStarknet {

// Panics
fn do_a_panic(self: @ContractState) {
let mut arr = ArrayTrait::new();
arr.append('PANIC');
arr.append('DAYTAH');
panic(arr);
panic(array!['PANIC', 'DAYTAH']);
}

fn do_a_string_panic(self: @ContractState) {
assert!(false, "This is panicking with a string, which can be longer than 31 characters");
// A macro which allows panicking with a ByteArray (string) instance
panic!("This is panicking with a string, which can be longer than 31 characters");
}
}
}
Expand All @@ -139,11 +139,9 @@ If we called this function in a test, it would result in a failure.

```rust
#[test]
#[feature("safe_dispatcher")]
fn failing() {
// ...

let (contract_address, _) = contract.deploy(@calldata).unwrap();
let contract = declare("HelloStarknet").unwrap();
let (contract_address, _) = contract.deploy(@array![]).unwrap();
let dispatcher = IHelloStarknetDispatcher { contract_address };

dispatcher.do_a_panic();
Expand All @@ -169,19 +167,25 @@ Failures:
### `SafeDispatcher`

Using `SafeDispatcher` we can test that the function in fact panics with an expected message.
Safe dispatcher is a special kind of dispatcher, which are not allowed in contracts themselves,
but are available for testing purposes.

They allow using the contract without automatically unwrapping the result, which allows to catch the error like shown below.

```rust
// Add those to import safe dispatchers, which are autogenerated, like regular dispatchers
use using_dispatchers::{ IHelloStarknetSafeDispatcher, IHelloStarknetSafeDispatcherTrait };

#[test]
#[feature("safe_dispatcher")]
fn handling_errors() {
// ...

let contract = declare("HelloStarknet").unwrap();
let (contract_address, _) = contract.deploy(@calldata).unwrap();
let safe_dispatcher = IHelloStarknetSafeDispatcher { contract_address };


match safe_dispatcher.do_a_panic() {
Result::Ok(_) => panic_with_felt252('shouldve panicked'),
Result::Ok(_) => panic!("Entrypoint did not panic"),
Result::Err(panic_data) => {
assert(*panic_data.at(0) == 'PANIC', *panic_data.at(0));
assert(*panic_data.at(1) == 'DAYTAH', *panic_data.at(1));
Expand All @@ -207,20 +211,20 @@ Similarly, you can handle the panics which use `ByteArray` as an argument (like
// Necessary utility function import
use snforge_std::byte_array::try_deserialize_bytearray_error;

// ...
#[test]
#[feature("safe_dispatcher")]
fn handling_string_errors() {
// ...
let (contract_address, _) = contract.deploy(@calldata).unwrap();
let contract = declare("HelloStarknet").unwrap();
let (contract_address, _) = contract.deploy(@array![]).unwrap();
let safe_dispatcher = IHelloStarknetSafeDispatcher { contract_address };

match safe_dispatcher.do_a_panic_with_bytearray() {
Result::Ok(_) => panic_with_felt252('shouldve panicked'),
match safe_dispatcher.do_a_string_panic() {
Result::Ok(_) => panic!("Entrypoint did not panic"),
Result::Err(panic_data) => {
let str_err = try_deserialize_bytearray_error(panic_data.span()).expect('wrong format');
assert(
str_err == "This is a very long\n and multiline message that is certain to fill the buffer",
str_err == "This is panicking with a string, which can be longer than 31 characters",
'wrong string received'
);
}
Expand All @@ -231,7 +235,7 @@ You also could skip the de-serialization of the `panic_data`, and not use `try_d
> 📝 **Note**
>
> To operate with `SafeDispatcher` it's required to annotage its usage with `#[feature("safe_dispatcher")]`.
> To operate with `SafeDispatcher` it's required to annotate its usage with `#[feature("safe_dispatcher")]`.
>
> There are 3 options:
> - module-level declaration
Expand Down

0 comments on commit 4f3e24f

Please sign in to comment.