docs: mock-provider (#2011)
* MockProvider example + mdbook reference * review: use eyre::Result Co-authored-by: Andrea Simeoni <>
This commit is contained in:
parent
3ed83d5dd3
commit
7e6c3ba983
|
@ -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)
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
# Mock provider
|
||||
|
||||
```rust
|
||||
{{#include ../../examples/providers/examples/mock.rs}}
|
||||
```
|
|
@ -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
|
||||
|
|
|
@ -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<P> {
|
||||
provider: Provider<P>,
|
||||
}
|
||||
|
||||
impl<P> OddBlockOracle<P>
|
||||
where
|
||||
P: JsonRpcClient,
|
||||
{
|
||||
fn new(provider: Provider<P>) -> Self {
|
||||
Self { provider }
|
||||
}
|
||||
|
||||
/// We want to test this!
|
||||
async fn is_odd_block(&self) -> eyre::Result<bool> {
|
||||
let block: U64 = self.provider.get_block_number().await?;
|
||||
Ok(block % 2 == U64::zero())
|
||||
}
|
||||
}
|
|
@ -4,20 +4,18 @@ use ethers::prelude::*;
|
|||
|
||||
const WSS_URL: &str = "wss://mainnet.infura.io/ws/v3/c60b0bb42f8a4c6481ecd229eddaca27";
|
||||
|
||||
type BoxErr = Box<dyn std::error::Error>;
|
||||
|
||||
/// The Ws transport allows you to send JSON-RPC requests and receive responses over WebSocket
|
||||
/// connections. It is useful for connecting to Ethereum nodes that support WebSockets.
|
||||
/// This allows to interact with the Ethereum network in real-time without the need for HTTP
|
||||
/// polling.
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), BoxErr> {
|
||||
async fn main() -> eyre::Result<()> {
|
||||
create_instance().await?;
|
||||
watch_blocks().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn create_instance() -> Result<(), BoxErr> {
|
||||
async fn create_instance() -> eyre::Result<()> {
|
||||
// An Ws provider can be created from an ws(s) URI.
|
||||
// In case of wss you must add the "rustls" or "openssl" feature
|
||||
// to the ethers library dependency in `Cargo.toml`.
|
||||
|
@ -39,7 +37,7 @@ async fn create_instance() -> Result<(), BoxErr> {
|
|||
}
|
||||
|
||||
/// Let's show how the Ws connection enables listening for blocks using a persistent TCP connection
|
||||
async fn watch_blocks() -> Result<(), BoxErr> {
|
||||
async fn watch_blocks() -> eyre::Result<()> {
|
||||
let provider = Provider::<Ws>::connect(WSS_URL).await?;
|
||||
let mut stream = provider.watch_blocks().await?.take(1);
|
||||
|
||||
|
|
Loading…
Reference in New Issue