pub struct MockBot<Err, Key> {
pub bot: Bot,
pub handler_tree: UpdateHandler<Err>,
pub updates: Vec<Update>,
pub me: Me,
pub dependencies: DependencyMap,
pub stack_size: usize,
/* private fields */
}Expand description
A mocked bot that sends requests to the fake server
Please check the new function docs and github examples for more information.
If you are having troubles with generics while trying to store MockBot, just do this:
MockBot<Box<dyn std::error::Error + Send + Sync>, teloxide_tests::mock_bot::DistributionKey>
Fields§
§bot: BotThe bot with a fake server url
handler_tree: UpdateHandler<Err>The thing that dptree::entry() returns
updates: Vec<Update>Updates to send as user
me: MeBot parameters are here
dependencies: DependencyMapIf you have something like a state, you should add the storage here using .dependencies()
stack_size: usizeThe stack size of the runtime for running updates
Implementations§
Source§impl<Err> MockBot<Err, DistributionKey>
impl<Err> MockBot<Err, DistributionKey>
Sourcepub fn new<T>(update: T, handler_tree: UpdateHandler<Err>) -> Selfwhere
T: IntoUpdate,
Err: Debug,
pub fn new<T>(update: T, handler_tree: UpdateHandler<Err>) -> Selfwhere
T: IntoUpdate,
Err: Debug,
Creates a new MockBot, using something that can be turned into Updates, and a handler tree. You can’t create a new bot while you have another bot in scope. Otherwise you will have a lot of race conditions. If you still somehow manage to create two bots at the same time (idk how), please look into this crate for serial testing
The update is just any Mock type, like MockMessageText or MockCallbackQuery or
vec![MockMessagePhoto] if you want! All updates will be sent consecutively and asynchronously.
The handler_tree is the same as in dptree::entry(), you will need to make your handler
tree into a separate function, like this:
use teloxide::dispatching::UpdateHandler;
fn handler_tree() -> UpdateHandler<Box<dyn std::error::Error + Send + Sync + 'static>> {
teloxide::dptree::entry() /* your handlers go here */
}§Full example
use teloxide::dispatching::UpdateHandler;
use teloxide::types::Update;
use teloxide_tests::{MockBot, MockMessageText};
use teloxide::dispatching::dialogue::GetChatId;
use teloxide::prelude::*;
fn handler_tree() -> UpdateHandler<Box<dyn std::error::Error + Send + Sync + 'static>> {
teloxide::dptree::entry().endpoint(|update: Update, bot: Bot| async move {
bot.send_message(update.chat_id().unwrap(), "Hello!").await?;
Ok(())
})
}
#[tokio::main] // Change for tokio::test in your implementation
async fn main() {
let mut bot = MockBot::new(MockMessageText::new().text("Hi!"), handler_tree());
bot.dispatch().await;
let responses = bot.get_responses();
let message = responses
.sent_messages
.last()
.expect("No sent messages were detected!");
assert_eq!(message.text(), Some("Hello!"));
}Source§impl<Err, Key> MockBot<Err, Key>
impl<Err, Key> MockBot<Err, Key>
Sourcepub fn new_with_distribution_function<T>(
update: T,
handler_tree: UpdateHandler<Err>,
f: fn(&Update) -> Option<Key>,
) -> Selfwhere
T: IntoUpdate,
Err: Debug,
pub fn new_with_distribution_function<T>(
update: T,
handler_tree: UpdateHandler<Err>,
f: fn(&Update) -> Option<Key>,
) -> Selfwhere
T: IntoUpdate,
Err: Debug,
Same as new, but it inserts a distribution_function into the dispatcher
Sourcepub fn dependencies(&mut self, deps: DependencyMap)
pub fn dependencies(&mut self, deps: DependencyMap)
Sets the dependencies of the dptree. The same as deps![] in bot dispatching.
Just like in this teloxide example: https://github.com/teloxide/teloxide/blob/master/crates/teloxide/examples/dialogue.rs
You can use it to add dependencies to your handler tree.
For more examples - look into get_state method documentation
Sourcepub fn me(&mut self, me: MockMe)
pub fn me(&mut self, me: MockMe)
Sets the bot parameters, like supports_inline_queries, first_name, etc.
Sourcepub fn update<T: IntoUpdate>(&mut self, update: T)
pub fn update<T: IntoUpdate>(&mut self, update: T)
Sets the updates. Useful for reusing the same mocked bot instance in different tests
Reminder: You can pass in vec![MockMessagePhoto] or something else!
Sourcepub fn error_handler(
&mut self,
handler: Arc<dyn ErrorHandler<Err> + Send + Sync>,
)
pub fn error_handler( &mut self, handler: Arc<dyn ErrorHandler<Err> + Send + Sync>, )
Sets the error_handler for Dispather
Sourcepub async fn dispatch(&mut self)
pub async fn dispatch(&mut self)
Actually dispatches the bot, calling the update through the handler tree.
All the requests made through the bot will be stored in responses, and can be retrieved
with get_responses. All the responses are unique to that dispatch, and will be erased for
every new dispatch.
This method overrides env variables TELOXIDE_TOKEN and TELOXIDE_API_URL, so anyone can
call Bot::from_env() and get an actual bot that is connected to the fake server
Sourcepub fn get_responses(&self) -> Responses
pub fn get_responses(&self) -> Responses
Returns the responses stored in responses
Should be treated as a variable, because it kinda is
Sourcepub async fn set_state<S>(&self, state: S)
pub async fn set_state<S>(&self, state: S)
Sets the state of the dialogue, if the storage exists in dependencies Panics if no storage was found
The only supported storages are InMemStorage and ErasedStorage,
using raw storages without .erase() is not supported.
For example on how to make ErasedStorage from RedisStorage or SqliteStorage go to this teloxide example
§Example
use teloxide::dispatching::UpdateHandler;
use teloxide::types::Update;
use teloxide_tests::{MockBot, MockMessageText};
use teloxide::dispatching::dialogue::GetChatId;
use teloxide::prelude::*;
use teloxide::{
dispatching::{
dialogue::{self, InMemStorage},
UpdateFilterExt,
}
};
use dptree::deps;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
enum State {
#[default]
Start,
NotStart
}
type MyDialogue = Dialogue<State, InMemStorage<State>>;
fn handler_tree() -> UpdateHandler<Box<dyn std::error::Error + Send + Sync + 'static>> {
dialogue::enter::<Update, InMemStorage<State>, State, _>().endpoint(|update: Update, bot: Bot, dialogue: MyDialogue| async move {
let message = bot.send_message(update.chat_id().unwrap(), "Hello!").await?;
dialogue.update(State::NotStart).await?;
Ok(())
})
}
#[tokio::main]
async fn main() {
let mut bot = MockBot::new(MockMessageText::new().text("Hi!"), handler_tree());
bot.dependencies(deps![InMemStorage::<State>::new()]);
bot.set_state(State::Start).await;
// Yes, Start is the default state, but this just shows how it works
bot.dispatch().await;
let state: State = bot.get_state().await;
// The `: State` type annotation is nessessary! Otherwise the compiler wont't know, what to return
assert_eq!(state, State::NotStart);
let responses = bot.get_responses();
let message = responses
.sent_messages
.last()
.expect("No sent messages were detected!");
assert_eq!(message.text(), Some("Hello!"));
}Sourcepub async fn assert_state<S>(&self, state: S)
pub async fn assert_state<S>(&self, state: S)
Helper function to fetch the state of the dialogue and assert its value
Sourcepub async fn get_state<S>(&self) -> S
pub async fn get_state<S>(&self) -> S
Gets the state of the dialogue, if the storage exists in dependencies
Panics if no storage was found
You need to use type annotation to get the state, please refer to the set_state
documentation example
Sourcepub async fn try_get_state<S>(&self) -> Option<S>
pub async fn try_get_state<S>(&self) -> Option<S>
Same as get_state, but returns None if the state is None, instead of the default
Sourcepub async fn dispatch_and_check_last_text(&mut self, text_or_caption: &str)
pub async fn dispatch_and_check_last_text(&mut self, text_or_caption: &str)
Dispatches and checks the last sent message text or caption. Pass in an empty string if you want the text or caption to be None
Sourcepub async fn dispatch_and_check_last_text_and_state<S>(
&mut self,
text_or_caption: &str,
state: S,
)
pub async fn dispatch_and_check_last_text_and_state<S>( &mut self, text_or_caption: &str, state: S, )
Same as dispatch_and_check_last_text, but also checks the state. You need to derive
PartialEq, Clone and Debug for the state like in set_state example
Sourcepub async fn dispatch_and_check_last_text_and_state_discriminant<S>(
&mut self,
text_or_caption: &str,
state: S,
)
pub async fn dispatch_and_check_last_text_and_state_discriminant<S>( &mut self, text_or_caption: &str, state: S, )
Same as dispatch_and_check_last_text, but also checks, if the variants of the state are the same
For example, State::Start { some_field: "value" } and State::Start { some_field: "other value" } are the same in this function
Sourcepub async fn dispatch_and_check_state<S>(&mut self, state: S)
pub async fn dispatch_and_check_state<S>(&mut self, state: S)
Just checks the state after dispathing the update, like dispatch_and_check_last_text_and_state
Auto Trait Implementations§
impl<Err, Key> !Freeze for MockBot<Err, Key>
impl<Err, Key> !RefUnwindSafe for MockBot<Err, Key>
impl<Err, Key> !Send for MockBot<Err, Key>
impl<Err, Key> !UnwindSafe for MockBot<Err, Key>
impl<Err, Key> Sync for MockBot<Err, Key>
impl<Err, Key> Unpin for MockBot<Err, Key>
impl<Err, Key> UnsafeUnpin for MockBot<Err, Key>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> Erasable for T
impl<T> Erasable for T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more