diff --git a/book/SUMMARY.md b/book/SUMMARY.md index 732d33c7..d3461cd5 100644 --- a/book/SUMMARY.md +++ b/book/SUMMARY.md @@ -9,7 +9,7 @@ - [Providers]() - [Http](./providers/http.md) - [IPC](./providers/ipc.md) - - [Mock]() + - [Mock](./providers/mock.md) - [Quorum](./providers/quorum.md) - [Retry](./providers/retry.md) - [RW](./providers/rw.md) diff --git a/book/providers/mock.md b/book/providers/mock.md new file mode 100644 index 00000000..2363bf71 --- /dev/null +++ b/book/providers/mock.md @@ -0,0 +1,5 @@ +# Mock provider + +```rust +{{#include ../../examples/providers/examples/mock.rs}} +``` \ No newline at end of file diff --git a/examples/README.md b/examples/README.md index 8bc4d73d..bb25a09d 100644 --- a/examples/README.md +++ b/examples/README.md @@ -34,10 +34,10 @@ - [x] Signer - [ ] Time lag - [ ] Transformer -- [ ] Providers +- [x] Providers - [x] Http - [x] IPC - - [ ] Mock + - [x] Mock - [x] Quorum - [x] Retry - [x] RW diff --git a/examples/providers/examples/mock.rs b/examples/providers/examples/mock.rs new file mode 100644 index 00000000..4d80fe57 --- /dev/null +++ b/examples/providers/examples/mock.rs @@ -0,0 +1,80 @@ +use ethers::prelude::*; + +/// `MockProvider` is a mock Ethereum provider that can be used for testing purposes. +/// It allows to simulate Ethereum state and behavior, by explicitly instructing +/// provider's responses on client requests. +/// +/// This can be useful for testing code that relies on providers without the need to +/// connect to a real network or spend real Ether. It also allows to test code in a +/// deterministic manner, as you can control the state and behavior of the provider. +/// +/// In these examples we use the common Arrange, Act, Assert (AAA) test approach. +/// It is a useful pattern for well-structured, understandable and maintainable tests. +#[tokio::main] +async fn main() -> eyre::Result<()> { + mocked_block_number().await?; + mocked_provider_dependency().await?; + Ok(()) +} + +async fn mocked_block_number() -> eyre::Result<()> { + // Arrange + let mock = MockProvider::new(); + let block_num_1 = U64::from(1); + let block_num_2 = U64::from(2); + let block_num_3 = U64::from(3); + // Mock responses are organized in a stack (LIFO) + mock.push(block_num_1)?; + mock.push(block_num_2)?; + mock.push(block_num_3)?; + + // Act + let ret_block_3: U64 = JsonRpcClient::request(&mock, "eth_blockNumber", ()).await?; + let ret_block_2: U64 = JsonRpcClient::request(&mock, "eth_blockNumber", ()).await?; + let ret_block_1: U64 = JsonRpcClient::request(&mock, "eth_blockNumber", ()).await?; + + // Assert + assert_eq!(block_num_1, ret_block_1); + assert_eq!(block_num_2, ret_block_2); + assert_eq!(block_num_3, ret_block_3); + + Ok(()) +} + +/// Here we test the `OddBlockOracle` struct (defined below) that relies +/// on a Provider to perform some logics. +/// The Provider reference is expressed with trait bounds, enforcing lose coupling, +/// maintainability and testability. +async fn mocked_provider_dependency() -> eyre::Result<()> { + // Arrange + let (provider, mock) = crate::Provider::mocked(); + mock.push(U64::from(2))?; + + // Act + // Let's mock the provider dependency (we ❤️ DI!) then ask for the answer + let oracle = OddBlockOracle::new(provider); + let answer: bool = oracle.is_odd_block().await?; + + // Assert + assert!(answer); + Ok(()) +} + +struct OddBlockOracle
{ + provider: Provider
, +} + +impl
OddBlockOracle
+where + P: JsonRpcClient, +{ + fn new(provider: Provider
) -> Self {
+ Self { provider }
+ }
+
+ /// We want to test this!
+ async fn is_odd_block(&self) -> eyre::Result