Skip to main content

MockBot

Struct MockBot 

Source
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: Bot

The 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: Me

Bot parameters are here

§dependencies: DependencyMap

If you have something like a state, you should add the storage here using .dependencies()

§stack_size: usize

The stack size of the runtime for running updates

Implementations§

Source§

impl<Err> MockBot<Err, DistributionKey>
where Err: Debug + Send + Sync + 'static,

Source

pub fn new<T>(update: T, handler_tree: UpdateHandler<Err>) -> Self
where 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>
where Err: Debug + Send + Sync + 'static, Key: Hash + Eq + Clone + Send + 'static,

Source

pub fn new_with_distribution_function<T>( update: T, handler_tree: UpdateHandler<Err>, f: fn(&Update) -> Option<Key>, ) -> Self
where T: IntoUpdate, Err: Debug,

Same as new, but it inserts a distribution_function into the dispatcher

Source

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

Source

pub fn me(&mut self, me: MockMe)

Sets the bot parameters, like supports_inline_queries, first_name, etc.

Source

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!

Source

pub fn error_handler( &mut self, handler: Arc<dyn ErrorHandler<Err> + Send + Sync>, )

Sets the error_handler for Dispather

Source

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

Source

pub fn get_responses(&self) -> Responses

Returns the responses stored in responses Should be treated as a variable, because it kinda is

Source

pub async fn set_state<S>(&self, state: S)
where S: Send + 'static + Clone,

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!"));
}
Source

pub async fn assert_state<S>(&self, state: S)
where S: Send + Default + 'static + Clone + Debug + PartialEq,

Helper function to fetch the state of the dialogue and assert its value

Source

pub async fn get_state<S>(&self) -> S
where S: Send + Default + 'static + Clone,

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

Source

pub async fn try_get_state<S>(&self) -> Option<S>
where S: Send + 'static + Clone,

Same as get_state, but returns None if the state is None, instead of the default

Source

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

Source

pub async fn dispatch_and_check_last_text_and_state<S>( &mut self, text_or_caption: &str, state: S, )
where S: Send + Default + 'static + Clone + Debug + PartialEq,

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

Source

pub async fn dispatch_and_check_last_text_and_state_discriminant<S>( &mut self, text_or_caption: &str, state: S, )
where S: Send + PartialEq + Debug + Default + 'static + Clone,

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

Source

pub async fn dispatch_and_check_state<S>(&mut self, state: S)
where S: Send + Default + 'static + Clone + Debug + PartialEq,

Just checks the state after dispathing the update, like dispatch_and_check_last_text_and_state

Source

pub async fn dispatch_and_check_state_discriminant<S>(&mut self, state: S)
where S: Send + Debug + PartialEq + Default + 'static + Clone,

Just checks the state discriminant after dispathing the update, like dispatch_and_check_last_text_and_state_discriminant

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> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> Erasable for T

Source§

const ACK_1_1_0: bool = true

Whether this implementor has acknowledged the 1.1.0 update to unerase’s documented implementation requirements. Read more
Source§

unsafe fn unerase(this: NonNull<Erased>) -> NonNull<T>

Unerase this erased pointer. Read more
Source§

fn erase(this: NonNull<Self>) -> NonNull<Erased>

Turn this erasable pointer into an erased pointer. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more