From 5c80537c9a897ef01f8a5e0845e5970b75f2372d Mon Sep 17 00:00:00 2001 From: HampusM Date: Wed, 20 Jul 2022 12:48:00 +0200 Subject: test: add DI container unit tests --- syrette/Cargo.toml | 3 + syrette/src/di_container.rs | 267 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 264 insertions(+), 6 deletions(-) diff --git a/syrette/Cargo.toml b/syrette/Cargo.toml index 6575ef1..b8ea0b3 100644 --- a/syrette/Cargo.toml +++ b/syrette/Cargo.toml @@ -12,3 +12,6 @@ linkme = "0.3.0" once_cell = "1.4" error-stack = "0.1.1" +[dev_dependencies] +mockall = "0.11.1" + diff --git a/syrette/src/di_container.rs b/syrette/src/di_container.rs index 334c533..6982a10 100644 --- a/syrette/src/di_container.rs +++ b/syrette/src/di_container.rs @@ -15,19 +15,19 @@ use crate::provider::{FactoryProvider, IProvider, InjectableTypeProvider, Provid use crate::ptr::{FactoryPtr, InterfacePtr}; /// Binding builder for type `Interface` inside a [`DIContainer`]. -pub struct BindingBuilder<'a, Interface> +pub struct BindingBuilder<'di_container_lt, Interface> where Interface: 'static + ?Sized, { - di_container: &'a mut DIContainer, + di_container: &'di_container_lt mut DIContainer, interface_phantom: PhantomData, } -impl<'a, Interface> BindingBuilder<'a, Interface> +impl<'di_container_lt, Interface> BindingBuilder<'di_container_lt, Interface> where Interface: 'static + ?Sized, { - fn new(di_container: &'a mut DIContainer) -> Self + fn new(di_container: &'di_container_lt mut DIContainer) -> Self { Self { di_container, @@ -120,7 +120,7 @@ pub struct DIContainer bindings: HashMap>, } -impl<'a> DIContainer +impl DIContainer { /// Returns a new `DIContainer`. #[must_use] @@ -132,7 +132,7 @@ impl<'a> DIContainer } /// Returns a new [`BindingBuilder`] for the given interface. - pub fn bind(&'a mut self) -> BindingBuilder + pub fn bind(&mut self) -> BindingBuilder where Interface: 'static + ?Sized, { @@ -247,3 +247,258 @@ impl Default for DIContainer Self::new() } } + +#[cfg(test)] +mod tests +{ + use mockall::mock; + + use super::*; + use crate::errors::injectable::ResolveError; + + #[test] + fn can_bind_to() + { + trait IUserManager + { + fn add_user(&self, user_id: i128); + + fn remove_user(&self, user_id: i128); + } + + struct UserManager {} + + impl IUserManager for UserManager + { + fn add_user(&self, _user_id: i128) + { + // ... + } + + fn remove_user(&self, _user_id: i128) + { + // ... + } + } + + impl Injectable for UserManager + { + fn resolve( + _di_container: &DIContainer, + ) -> error_stack::Result< + InterfacePtr, + crate::errors::injectable::ResolveError, + > + where + Self: Sized, + { + Ok(InterfacePtr::new(Self {})) + } + } + + let mut di_container: DIContainer = DIContainer::new(); + + assert_eq!(di_container.bindings.len(), 0); + + di_container.bind::().to::(); + + assert_eq!(di_container.bindings.len(), 1); + } + + #[test] + fn can_bind_to_factory() + { + trait IUserManager + { + fn add_user(&self, user_id: i128); + + fn remove_user(&self, user_id: i128); + } + + struct UserManager {} + + impl UserManager + { + fn new() -> Self + { + Self {} + } + } + + impl IUserManager for UserManager + { + fn add_user(&self, _user_id: i128) + { + // ... + } + + fn remove_user(&self, _user_id: i128) + { + // ... + } + } + + type IUserManagerFactory = dyn IFactory<(), dyn IUserManager>; + + let mut di_container: DIContainer = DIContainer::new(); + + assert_eq!(di_container.bindings.len(), 0); + + di_container.bind::().to_factory(&|| { + let user_manager: InterfacePtr = + InterfacePtr::new(UserManager::new()); + + user_manager + }); + + assert_eq!(di_container.bindings.len(), 1); + } + + #[test] + fn can_get() -> error_stack::Result<(), DIContainerError> + { + trait IUserManager + { + fn add_user(&self, user_id: i128); + + fn remove_user(&self, user_id: i128); + } + + struct UserManager {} + + use crate as syrette; + use crate::injectable; + + #[injectable(IUserManager)] + impl UserManager + { + fn new() -> Self + { + Self {} + } + } + + impl IUserManager for UserManager + { + fn add_user(&self, _user_id: i128) + { + // ... + } + + fn remove_user(&self, _user_id: i128) + { + // ... + } + } + + mock! { + Provider {} + + impl IProvider for Provider + { + fn provide( + &self, + di_container: &DIContainer, + ) -> error_stack::Result; + } + } + + let mut di_container: DIContainer = DIContainer::new(); + + let mut mock_provider = MockProvider::new(); + + mock_provider.expect_provide().returning(|_| { + Ok(Providable::Injectable( + InterfacePtr::new(UserManager::new()), + )) + }); + + di_container + .bindings + .insert(TypeId::of::(), Rc::new(mock_provider)); + + di_container.get::()?; + + Ok(()) + } + + #[test] + fn can_get_factory() -> error_stack::Result<(), DIContainerError> + { + trait IUserManager + { + fn add_user(&mut self, user_id: i128); + + fn remove_user(&mut self, user_id: i128); + } + + struct UserManager + { + users: Vec, + } + + impl UserManager + { + fn new(users: Vec) -> Self + { + Self { users } + } + } + + impl IUserManager for UserManager + { + fn add_user(&mut self, user_id: i128) + { + self.users.push(user_id); + } + + fn remove_user(&mut self, user_id: i128) + { + let user_index = + self.users.iter().position(|user| *user == user_id).unwrap(); + + self.users.remove(user_index); + } + } + + use crate as syrette; + + #[crate::factory] + type IUserManagerFactory = dyn IFactory<(Vec,), dyn IUserManager>; + + mock! { + Provider {} + + impl IProvider for Provider + { + fn provide( + &self, + di_container: &DIContainer, + ) -> error_stack::Result; + } + } + + let mut di_container: DIContainer = DIContainer::new(); + + let mut mock_provider = MockProvider::new(); + + mock_provider.expect_provide().returning(|_| { + Ok(Providable::Factory(FactoryPtr::new(CastableFactory::new( + &|users| { + let user_manager: InterfacePtr = + InterfacePtr::new(UserManager::new(users)); + + user_manager + }, + )))) + }); + + di_container + .bindings + .insert(TypeId::of::(), Rc::new(mock_provider)); + + di_container.get_factory::()?; + + Ok(()) + } +} -- cgit v1.2.3-18-g5258