Replies: 3 comments 6 replies
-
On Mon, Jul 4, 2022 at 6:02 AM Chitoku ***@***.***> wrote:
Let's say I want to mock Service::get_by_ids() in the following and test
Application::run():
struct Foo;struct FooId(u32);
#[cfg_attr(test, mockall::automock)]trait Service: Send + Sync + 'static {
fn get_by_ids<T>(&self, ids: T) -> Vec<Foo>
where
T: IntoIterator<Item = FooId> + Send + Sync + 'static;
}
struct Application<T> {
service: T,
}
impl<T> Application<T>where
T: Service,
{
fn new(service: T) -> Self {
Self { service }
}
fn run(&self, ids: &[u32]) {
let ids = ids.into_iter().map(|id| FooId(id));
self.service.get_by_ids(ids);
}
}
#[cfg(test)]mod tests {
use super::*;
#[test]
fn run() {
let mut mock_service = MockService::new();
mock_service
.expect_get_by_ids::</* no idea what could be written here */>()
.returning(|_| Vec::new());
let application = Application::new(mock_service);
application.run(&[1, 2, 3, 4]);
}
}
I'd like to test this while get_by_ids() being mocked, but neither of the
following does not work:
- std::iter::Map<std::vec::IntoIter<u32>, fn(u32) -> FooId>
- ends up with No matching expectation found
- core::iter::adapters::map::Map<alloc::vec::into_iter::IntoIter<u32>,
fn(u32) -> FooId>
- This is extracted from the output of std::any::type_name() but it
doesn't compile as core::iter::adapters is a private module
How do I mock this, hopefully without writing a concrete type name that
represents implementation. Thanks!
mockall: 0.11.1
rustc: 1.62.0 (a8314ef7d 2022-06-27)
Rust edition: 2021
Message ID: ***@***.***>
Try this:
```rust
let ids: i32 = ids.into_iter().map(|id| FooId(id));
self.service.get_by_ids(ids);
```
The compiler's error message should show you what the correct type name is.
And if it includes something like `core::iter` that won't compile, try
replacing it with `std::iter`.
|
Beta Was this translation helpful? Give feedback.
1 reply
-
On Mon, Jul 4, 2022 at 8:18 AM Chitoku ***@***.***> wrote:
Thank you for the quick answer!
As I tested further based on your suggestion, the culprit seems to be how
a closure is interpreted. As an experiment I changed the implementation not
to use .map() but instead .rev() to find out the root cause:
let ids = ids.into_iter().rev();
and the annotation becomes std::iter::Rev<std::vec::IntoIter<FooId>>
mock_service
.expect_get_by_ids::<std::iter::Rev<std::vec::IntoIter<FooId>>>()
.returning(|_| Vec::new());
then the mocking succeeds; however, once I use a closure or even a
function, the compiler emits an error message in a way that a programmer
cannot directly embed in Rust code:
= note: expected type `i32`
found struct `std::iter::Map<std::vec::IntoIter<u32>, ***@***.***/main.rs:58:42: 58:54]>`
= note: expected type `i32`
found struct `std::iter::Map<std::vec::IntoIter<u32>, fn(u32) -> FooId {convert_u32_to_foo_id_func}>`
—
Reply to this email directly, view it on GitHub
<#391 (reply in thread)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAA7VOJQIJH6RNEW4GKS4LLVSLXCXANCNFSM52S6XQWA>
.
You are receiving this because you commented.
Ahh yes. Closures can never be named. Therefore you can't set a Mockall
expectation for a call with a closure. The generic workaround is to turn
the closure into a named function. Or, you can Box the `ids` argument and
turn it into a trait object: `Box<dyn IntoIterator<...>>`. But there's an
easier alternative for this specific case, because you don't even need a
closure. You can just turn `map(|id| FooId(id)` into `map(FooId)`.
… Message ID: ***@***.***
com>
|
Beta Was this translation helpful? Give feedback.
1 reply
Answer selected by
chitoku-k
-
As a workaround, I manually implemented every function for a trait that firstly collects #[cfg(test)]
struct MockServiceWrapper(MockService);
#[cfg(test)]
impl MockServiceWrapper {
fn new() -> Self {
Self(MockService::new())
}
}
#[cfg(test)]
impl Service for MockServiceWrapper {
fn get_by_ids<T>(&self, ids: T) -> Vec<Foo>
where
T: IntoIterator<Item = FooId> + Send + Sync + 'static,
{
let ids: Vec<_> = ids.into_iter().collect();
self.0.get_by_ids(ids).collect()
}
}
// Implement Deref and DerefMut for MockServiceWrapper too.
// ... This isn't pretty much ideal but at least it works and does not rely on the exact type that caller specified. Any other solution would be still welcomed but I think I could've made this to work. |
Beta Was this translation helpful? Give feedback.
4 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Let's say I want to mock
Service::get_by_ids()
in the following and testApplication::run()
:I'd like to test this while
get_by_ids()
being mocked, but neither of the following does not work:std::iter::Map<std::vec::IntoIter<u32>, fn(u32) -> FooId>
No matching expectation found
core::iter::adapters::map::Map<alloc::vec::into_iter::IntoIter<u32>, fn(u32) -> FooId>
std::any::type_name()
but it doesn't compile ascore::iter::adapters
is a private moduleHow do I mock this, hopefully without writing a concrete type name that represents implementation? Thanks!
mockall: 0.11.1
rustc: 1.62.0 (a8314ef7d 2022-06-27)
Rust edition: 2021
Beta Was this translation helpful? Give feedback.
All reactions