안녕하세요 러스트조빱이에요


trait의 구현체의 동작이 모두 똑같을 때 이미 작성되어있는 테스트 코드를 재활용하고 싶어서 생각하다가 잘 돼서 조빱이지만 글 써봐요


#[derive(Debug, PartialEq)] struct User { username: String, } trait UserRepository { fn get_user_by_username(&self, username: String) -> Option<&User>; } struct InMemoryUserRepository { inner: Vec<User>, } impl UserRepository for InMemoryUserRepository { fn get_user_by_username(&self, username: String) -> Option<&User> { self.inner.iter().find(|user| user.username == username) } }



서버를 만들고 있다고 가정하고, 처음에는 테스트만 하면 되기 때문에 인메모리로도 충분해요.


그런데 점차 개발이 진행되고, 데이터 베이스를 사용해야하는 시점이 왔어요.


struct DatabaseUserRepository { inner: HashMap<String, User>, } impl UserRepository for DatabaseUserRepository { fn get_user_by_username(&self, username: String) -> Option<&User> { self.inner.get(&username) } }


구현했어요. 테스트도 해야돼요.


trait TestUserRepository: UserRepository + From<Vec<User>> { fn test_get_user_by_username() { let user_repository = Self::from(fake_users()); let actual = user_repository.get_user_by_username("홍길동".to_string()); let expected = User { username: "홍길동".to_string(), }; assert_eq!(actual, Some(&expected)); } } impl From<Vec<User>> for DatabaseUserRepository { fn from(users: Vec<User>) -> Self { let mut inner = HashMap::default(); for user in users { let username = user.username.clone(); inner.insert(username, user); } Self { inner } } } impl From<Vec<User>> for InMemoryUserRepository { fn from(users: Vec<User>) -> Self { Self { inner: users } } } impl TestUserRepository for InMemoryUserRepository {} impl TestUserRepository for DatabaseUserRepository {}


TestUserRepository trait은 UserRepository와 From을 구현하게해서 테스트에서 해당 구현체를 생성할 수 있어요.

그런 다음 TestUserRepository trait을 각각 Repository에 구현해줘요.


mod inmemory { use super::InMemoryUserRepository; use super::TestUserRepository; #[test] fn test_get_user_by_username() { InMemoryUserRepository::test_get_user_by_username() } } mod database { use super::DatabaseUserRepository; use super::TestUserRepository; #[test] fn test_get_user_by_username() { DatabaseUserRepository::test_get_user_by_username() } }


이렇게 해주면 이미 작성돼있는 테스트 코드를 재활용할 수 있어요.