-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Generics in mocked methods #18
Comments
I am actually running into this with code similar to this:
Is there a known workaround how I can mock this code still? |
The only workaround I can think of is not mocking this method, but mocking things that this method calls. #[faux::methods]
impl MyStruct {
// this can be mocked
pub fn metadata(&self) -> files::Metadata {
/* get it somehow */
}
}
impl MyStruct {
// this cannot be mocked so it is in a separate impl block.
pub fn list_directory(&self, callback: impl fn(files::Metadata) {
// the call to metadata may have been mocked.
let metadata = self.metadata();
callback.call(metadata);
}
} The main thing to note is that in the non-mocked methods, you may not access any fields in the struct. It is not very nice but at least it lets you get around the issue somewhat, and you can still mock the other methods on your struct. Thanks for bringing up that you run into this! It helps me prioritize what to do next. |
Ah, unfortunately that did not help me, since I really needed the callback based API in my use case. I went with pulling my API out into a trait and manually mock the behavior of this trade now. Good enough for my uses for me, but I eagerly await mock becoming more powerful! Thanks for your work, this all seems super promising. |
Would changing your signature to use generics rather than Changing: pub fn list_directory(&self, ... callback: impl Fn(files::Metadata)) to: pub fn list_directory<F: Fn(files::Metadata)>(&self, ... callback: F) |
I would not mind doing that for easier mocking in future cases - the loss of ergonomics in this case is minimal and the gain of direct mocking substantial, so totally worth it. |
Apologies for the long turn around time, life has been a little crazy and this actually ended up being a bit of a mind melt. As it turns out it is actually easier for My current solution (a26cf09) wraps all Unfortunately this is the only way I can think of at the moment to allow for this in a "safe" manner. I think I might just release this as-is to see if it solves your use case with the caveat that if I think of a better solution I might end up changing the API of it. |
Implementation Issues (writing for my own sake, no need to read)UnsafetyFor methods such as: pub fn my_method<T>(&self, a: T) {} My first though for a fn _when_my_method<T>(&self) -> When<(T), ()> {
/* make a When */
} The fn then(self, mock: impl FnMut(T) -> O + 'static + Send) {
/* store the mock */
} At this point the user can now pass a mock that assumes any user-specified T: mocked_struct._when_my_method<i32>().safe_then(|i: i32| {}) This seems all fine and dandy until... the user calls for the method: mocked_struct.my_method("a string is still a T this method accepts"); And now we are big trouble. Unnameable typesGiven a structure similar to above, how would someone declare a mock for: pub fn my_callback<F: Fn(i32)>(&self, a: F) {} When method: fn _when_my_method<F: Fn(i32)>(&self) -> When<(T), ()> {
/* make a When */
} When the user calls for when: mocked_struct._when_my_method</* what goes here? */>()
.safe_then(|i: /* what type is this? */| {}) I don't believe there is a way to name concrete types to closures as generic bounds so.... this would not work. AlternativesExclude and ignore generic typesSomehow (?) detect what arguments are generics, and exclude them from the Box all generic typesSomehow (?) detect what arguments are generics, box them all up. This means make the These two depend on finding what the trait arguments are which is not easy task for more complicated cases but it is perhaps doable, I would have to try it out. The |
I clicked the wrong button, this is definitely not closed. |
Thanks for you work! I definitely make use of this! |
I think I'm hitting this with #[cfg_attr(test, faux::methods)]
impl S {
pub async fn foo<M>(&self, message: &M) -> Result<(), Error>
where
M: Message + Serialize + Sync,
{
// ...
}
|
If possible, this should work by switching to using the pub async fn foo<M>(&self, message: &impl Message + Serialize + Sync) -> Result<(), Error> {
// ...
} That being said, I should look into this since it is not always possible to use the |
Interesting side discovery: faux forces all function Trying to please faux with use std::fmt::Debug;
trait A: Debug {}
#[cfg_attr(test, faux::create)]
struct S {}
#[cfg_attr(test, faux::methods)]
impl S {
pub fn foo(&self, _a: &impl A) {
println!("foo")
}
}
fn main() {
#[derive(Debug)]
struct T(u8);
impl A for T {}
let t = T(42);
let s = S {};
s.foo(&t);
}
#[cfg(test)]
#[test]
fn repro() {
let mut mock = S::faux();
faux::when!(mock.foo).then(|_| println!("mock foo"));
}
|
Ah! 🤦🏾♂️ That's totally my bad. I just realized my That being said, the trait does not need to implement debug at all. See the tests. Additionally, this part of the error:
has to do with the your main function trying to create an |
Ah yes, adding an |
I have a fix up but I was having issues with releasing yesterday, my plan is to get a release for this today or tomorrow. You can see the updated tests and let me know if you think it isn't covering your use case: https://github.com/nrxus/faux/blob/master/tests/generic_methods.rs |
@tv42 I just published a new release that should have this fix. Try it out and let me know! (: |
Hi! I have a function that's signature is:
And found no workaround to make it build |
@nrxus any ideas? |
Hey @jmoguilevsky, things get hectic during the holidays so I wasn't looking at Hm this use case isn't supported by For a workaround for now, if possible, I would try to have that method in an #[cfg_attr(test, faux::create)]
struct Foo {
/* snip */
}
// THIS ONE IS WRAPPED BY `faux::methods`
#[cfg_attr(test, faux::methods)]
impl Foo {
async fn inner_authenticated_request(
&self,
path: &str,
token: &str,
) -> Result<String, CommonError> {
/* snip your implementation */
}
}
// NOT WRAPPED BY `faux::methods`
impl Foo {
pub async fn get_authenticated_request<T: DeserializeOwned>(
&self,
path: &str,
token: &str,
) -> Result<T, CommonError> {
let raw = self.inner_authenticated_request().await?;
/* snip convert raw string to deserialized T */
}
} |
Partial fix fo nrxus#18 This changeset allows generics to method return types and arguments. Generic impl traits are still not supported (yet).
Partial fix fo nrxus#18 This changeset allows generics to method return types and arguments. Generic impl traits are still not supported in return position (yet?).
Neither of these work.
The issue is on the definition of the
_when_{}
method.The first two seem solvable together. Add all the generics from the mocked method to the
when
method.The third one seems like it would require "de-sugaring" the
impl Foo
into<T: Foo>
for thewhen
method only.The last one.... 🤷♂️ We could change it into a generic argument to the
when
method as a first-step MVP (read: hack).The text was updated successfully, but these errors were encountered: