블로그
전체 72024. 10. 25.
0
워밍업 클럽 2기 BE 클린코드&테스트 발자국 4주차
강의 : Practical Testing: 실용적인 테스트 가이드@Mock객체가 기대하는 행위와 리턴 값을 지정하여 테스트에서 기대하는 동작만 테스트가 가능하다주로 외부 네트워크를 사용하는 객체에 사용하면 좋다 @MockBean스프링 컨테이너에서 사용하기 위한 @Mock 객체@Spy일부는 Mock 객체처럼 동작만 그 이외에는 실제 객체처럼 동작한다내가 지정한 동작을 기대하는 행위, 반환에 대한 mocking 할 수 있다실제로 많이 쓰이지는 않는다@SpyBean스프링 컨테이너에서 사용하기 위한 @Spy 객체@InjectMocksMock, MockBean 객체를 주입 받는 대상 객체에 사용된다ex) OrderService에 UserService, ProductService 주입이 필요한 경우 BDDMockitogiven, when, then 3단계로 테스트 작성이 가능하다Classicist vs Mockist클래스를 직접 사용하여 테스트할지? Classicist 관심사 외엔 다 목킹하여 테스트할지? Mockist정답은 없지만 개인적인 내 생각으로는 객체간의 협력에 따른 결과가어떤 결과가 나올지 예상 불가하기에 최대한 실제 객체를 사용하는 Classicist가 좀 더 좋아보인다뭐가 정답이라기 보다는 관심사에는 Classicist, 관심사가 아닌 부분엔 Mockist을 적절하게 배치하는게 좋아보인다한 문단에 한 주제테스트 또한 문서의 성격을 띄기 때문에 하나의 테스트에 한 가지 기능만 테스트해야 가독성 좋다테스트 환경/테스트간의 독립성 을 보장하자어떤 환경이던, 어떤 순서로 실행하던 반복가능하며, 테스트가 성공으로 떠야 한다테스트 환경 통합테스트 환경을 빠르게 하기 위해서는 ApplicationContext가 띄우는 횟수를 줄여야 한다layer별로 상위 클래스를 만들고 그 상위 클래스를 상속하는 방식으로 ApplicationContext를 재사용하자private 메서드의 테스트?public 함수 호출 시 내부 private 메소드도 호출되기에 따로 작성 필요없다private 메서드의 테스트가 강력하게 필요하다 느끼면 그건 클래스 분리의 신호!!
백엔드
・
워밍업클럽2기
・
BE클린코드&테스트
2024. 10. 24.
0
워밍업 클럽 2기 BE 클린코드&테스트 day18 과제
강의 : Practical Testing: 실용적인 테스트 가이드@Mock 객체가 기대하는 행위와 리턴 값을 지정하여 테스트에서 기대하는 동작만 테스트가 가능하다주로 외부 네트워크를 사용하는 객체에 사용하면 좋다 @MockBean스프링 컨테이너에서 사용하기 위한 @Mock 객체@Spy 일부는 Mock 객체처럼 동작만 그 이외에는 실제 객체처럼 동작한다내가 지정한 동작을 기대하는 행위, 반환에 대한 mocking 할 수 있다실제로 많이 쓰이지는 않는다 @SpyBean 스프링 컨테이너에서 사용하기 위한 @Spy 객체@InjectMocksMock, MockBean 객체를 주입 받는 대상 객체에 사용된다ex) OrderService에 UserService, ProductService 주입이 필요한 경우 @MockBean private UserService userService; @MockBean private ProductService productService @InjectMocks private OrderService orderService;각 항목을 @BeforeEach, given절, when절에 배치한다면 어떻게 배치할지?@BeforeEach void setUp() { 사용자 생성에 필요한 내용 준비 게시물 생성에 필요한 내용 준비 댓글 생성에 필요한 내용 준비 } @DisplayName("사용자가 댓글을 작성할 수 있다.") @Test void writeComment() { // given 1-2. 사용자 생성 1-4. 게시물 생성 // when 1-6. 댓글 생성 // then 검증 } @DisplayName("사용자가 댓글을 수정할 수 있다.") @Test void updateComment() { // given 2-2. 사용자 생성 2-4. 게시물 생성 2-6. 댓글 생성 // when 2-7. 댓글 수정 // then 검증 } @DisplayName("자신이 작성한 댓글이 아니면 수정할 수 없다.") @Test void cannotUpdateCommentWhenUserIsNotWriter() { // given 3-2. 사용자1 생성 3-4. 사용자2 생성 3-6. 사용자1의 게시물 생성 3-8. 사용자1의 댓글 생성 // when 3-9. 사용자2가 사용자1의 댓글 수정 시도 // then 검증 }
백엔드
・
워밍업클럽2기
・
클린코드&테스트
2024. 10. 22.
0
워밍업 클럽 2기 BE 클린코드&테스트 day15 과제
강의 : Practical Testing: 실용적인 테스트 가이드 Persistence Layer각 작업이 올바르게 커밋 또는 롤백되는지 확인이 중요하다반복적으로 테스트 성공을 위해서는 데이터 클렌징이 중요하다 Business Layer주요 비즈니스 로직을 구현된 LayerPersistence Layer에 종속적일 수 밖에 없다given에 도메인 객체의 생성 로직이 복잡한 경우엔 도우미 메서드를 활용하자필요한 정보와 불필요한 정보를 분리하여 필요한 정보만 파라메터로 받도록 하자Presentation Layer외부 세계와의 연결하는 Controller를 테스트하는 LayerPersistence Layer, Business Layer는 mocking하여 테스트함주요 비즈니스 로직을 실행하기에 적절한지 파라메터 검증이 주요 목적파라메터 검증의 경우, 도메인 성격을 띈 내용과 아닌 내용을 분리비즈니스 성격을 띈 유효성 체크는 Business Layer비즈니스 성격이 아닌 경우는 Presentation Layer
백엔드
・
워밍업클럽2기
・
BE클린코드&테스트
2024. 10. 20.
0
워밍업 클럽 2기 BE 클린코드&테스트 발자국 3주차
강의 : Practical Testing: 실용적인 테스트 가이드테스트의 필요성잘 만든 테스트로 인하여 신뢰성 있는 소프트웨어를 만들 수 있다잘 만든 테스트로 인하여 과감한 수정 및 리팩토링이 가능하다테스트 코드 자체가 문서가 된다자동화 테스트와 JUnit테스트를 만드는 것 자체도 비용이 들기에 사람이 수동으로 하기 보다는 자동화 테스트가 필요함테스트를 빠르고 여러번 실행 가능하도록 하도록 JUnit이 돕는다 테스트 어려운 영역을 분리테스트는 반복가능하며 항상 성공하는 테스트여야 한다시간, 랜덤 등의 성격의 경우 매개변수로 주입하여서 테스트 힘든 부분을 분리하여 테스트가 가능하다TDD실패하는 테스트를 만든다테스트를 성공 시킨다리팩토링아래의 3단계를 짧은 사이클로 가져가면서 점차적으로 개발이 가능하다DisplayName테스트는 요구사항을 잘 드러내는 문서의 역할도 겸하기 때문에 Display name을 잘 지어야 한다~ 테스트가 아닌 문장으로 끝내며 주어, 목적, 결과가 다 드러나도록 기재해야 한다BDDgiven, when, then 3단계에 나눠거의 : Practical Testing: 실용적인 테스트 가이드 테스트의 필요성잘 만든 테스트로 인하여 신뢰성 있는 소프트웨어를 만들 수 있다잘 만든 테스트로 인하여 과감한 수정 및 리팩토링이 가능하다테스트 코드 자체가 문서가 된다자동화 테스트와 JUnit테스트를 만드는 것 자체도 비용이 들기에 사람이 수동으로 하기 보다는 자동화 테스트가 필요함테스트를 빠르고 여러번 실행 가능하도록 하도록 JUnit이 돕는다테스트 어려운 영역을 분리테스트는 반복가능하며 항상 성공하는 테스트여야 한다시간, 랜덤 등의 성격의 경우 매개변수로 주입하여서 테스트 힘든 부분을 분리하여 테스트가 가능하다TDD실패하는 테스트를 만든다테스트를 성공 시킨다리팩토링아래의 3단계를 짧은 사이클로 가져가면서 점차적으로 개발이 가능하다 DisplayName테스트는 요구사항을 잘 드러내는 문서의 역할도 겸하기 때문에 Display name을 잘 지어야 한다~ 테스트가 아닌 문장으로 끝내며 주어, 목적, 결과가 다 드러나도록 기재해야 한다BDDgiven, when, then 3단계에 나눠서 테스트를 작성하도록 한다given테스트에 필요한 객체 및 데이터를 생성한다when테스트하고자 하는 내용을 실행한다thenassertj 단언문을 이용하여 테스트하고자 하는 내용을 확인한다
백엔드
・
클린코드
・
테스트
・
워밍업클럽2기
2024. 10. 13.
0
워밍업 클럽 2기 BE 클린코드&테스트 발자국 2주차
강의 : Readable Code: 읽기 좋은 코드를 작성하는 사고법섹션 6 코드 다듬기주석의 양면성코드는 유지보수 되나 주석은 유지보수 되지 않는다코드의 설명을 위한 주석은 좋지 않다주석을 사용한다면 코드로 파악이 힘든 내용들을 담도록 하자히스토리 파악이 힘든 코드에 대한 히스토리 설명패키지 나누기의미있는 단위로 패키지를 나누자 섹션 7 리팩토링 연습코드 레벨에서의 추상화가 아닌 도메인을 잘 이해하여 높은 레벨로 추상화하여 가독성을 높이자 ex) 고정석이 아니면 return 관점을 고정석이 아닌 경우라 생각하면 doesNotFixed라 메소드 이름을 짓게 된다고정석이 아니라는 건 락커를 사용할 수 없다는 의미로 더 높은 추상화 레벨로 메소드 이름을 지으면 canNotUserLocker로 메소드 이름을 지을 수 있다클래스명에 대한 추상화 ex) 파일을 읽어 어떠한 정보를 가져오는 클래스클라이언트 입장에선 어떤 정보를 가져오는지에 대한 관심만 있음정보를 가져오는 수단에 대한 관심을 지우고 어떠한 정보를 가져올지에 포커스를 맞추다~Provider, ~Generator 등의 이름을 활용하도록 하자더 좋은 네이밍은 스프링에서 찾아보도록 하자 섹션 8 기억하면 좋은 조언들객체지향이 무조건 좋은 건 아니다과한 객체 지향은 오버엔지니어링이 될 수 있고 가독성을 해친다요구사항에 따라 절차적 프로그래밍이 어울릴 수 있고 객체지향 프로그래밍이 어울릴 수 있다내가 짠 코드에 근거를 가지고 상황에 맞게 리팩토링 기법이나 객체 지향을 잘 적용하도록 하자
백엔드
・
워밍업클럽2기
・
BE클린코드&테스트
2024. 10. 06.
0
워밍업 클럽 2기 BE 클린코드&테스트 발자국 1주차
강의 : Readable Code: 읽기 좋은 코드를 작성하는 사고법섹션 2 추상추상화를 코드 레벨에서 어떻게 풀어내는지? 이름 짓기가장 쉬운 추상화 방법이면서 가장 중요하다고 생각한다이름 하나만 잘 짓고 하나의 주제 단위로 메소드 추출만 잘 해도 가독성 좋은 코드가 된다메소드메소드명이 추상화에 중요한 건 알았지만 리턴타입, 파라메터, 파라메터 순서까지 신경쓰지는 않았었다리턴타입, 파라메터의 변수명을 활용하여 가독성 좋은 코드를 어떻게 만드는지 알 수 있었다 섹션 3 논리, 사고의 흐름가독성 좋은 코드를 위해서 사용할 수 있는 여러 기법들을 소개한다early return복잡한 분기가 있을 경우 공백 라인의미 단위로 공백 라인을 사용하면 가독성에 도움을 준다부정어에 대한 자세부정어를 긍정어로 바꿀 수 있다면 변경이름에서 부정을 나타내도록 변경메소드 내에서 같은 레벨로 추상화재사용성은 떨어지더라도 메소드 추출하여 하나의 메소드에서 같은 레벨로 추상화섹션 4 객체지향 패러다임SOLID를 코드 레벨에서 어떻게 구현할 수 있는지?SRP클래스가 변경될 이유는 단 하나다!!클래스 설계 시, 하나의 관심사로 응집하여 구현하도록 하자OCP기존 코드의 변경 없이, 기능 추가하도록 설계가 되어야 한다 다형성을 이용하여 클라이언트 코드의 변경 없이 구현체를 쉽게 변경이 가능하다LSP부모 클래스에서 정의한 스펙을 자식 클래스에서도 지켜야 한다instance of로 타입 체크하여 특정 타입에 대해서만 처리가 필요하다는 건 LSP를 위반한 사례이다!!ISPSRP와 비슷하게 인터페이스 또한 한 가지 책임을 가지도록 분리를 잘해야 한다DIP클래스 참조 시, 추상화 레벨이 높은 인터페이스, 추상클래스, 상위 클래스를 참조하도록 하자코드에 구현체가 드러나게 되면 기능 변경시, 클라이언트 코드의 변경이 생기므로추상화 레벨이 높은 인터페이스 등을 메소드 파라메터, 필드, 리턴타입으로 활용하도록 하자
백엔드
・
워밍업클럽2기
・
BE클린코드&테스트
・
발자국
2024. 10. 03.
0
워밍업 클럽 2기 BE 클린코드&테스트 day4 과제
Readable Code: 읽기 좋은 코드를 작성하는 사고법(링크)아래 내용은 위의 인프런 강의를 들으면서 과제 및 내용을 정리해봤습니다. 아래 코드 리팩토링 하기public boolean validateOrder(Order order){ if(order.getItems().size() == 0){ log.info("주문 항목이 없습니다"); }else{ if(order.getTotalPrice() > 0){ if(!order.hasCustomerInfo()){ log.info("사용자 정보가 없습니다"); return false; }else{ return true; } }else if(!(order.getTotalPrice() > 0)){ log.info("올바르지 않은 총 가격입니다"); return false; } } return true; }본 코드의 문제는 아래와 같다if, else if, else 그리고 중접 if 등으로 인하여 주문의 유효성 조건이 한 눈에 들어오지 않는다=> early return을 사용해보자 무분별한 getter의 사용(if(order.getItems().size() == 0))=> 주문에 담긴 상품이 있는지 확인하는 내용인데 굳이 getter를 써야할까?=> 오히려 order에 item 항목이 있다고 세부 내용을 노출해버리고 있다=> 의미있는 이름의 메소드를 order에서 그 역할을 책임하도록 해보자 부정어의 사용 if(order.hasCustomerInfo()) / !(order.getTotalPrice() > 0)=> 부정어를 사용하면 코드의 가독성을 해친다 => 1순위 긍정어를 사용하여 메소드 만들 수 있는지 확인=> 2순위 긍정어를 사용할 수 없다면 메소드명으로 부정을 나타내서 만들어보자 위의 내용을 고려한 최종 코드는 아래와 같다public boolean validateOrder(Order order){ if(order.isEmptyOrderItem()){ log.info("주문 항목이 없습니다"); return false; } if(order.isLessThanOrEqualToZero()){ log.info("올바르지 않은 총 가격입니다"); return false; } if(order.isEmptyCustomerInfo()){ log.info("사용자 정보가 없습니다"); return false; } return true; }public class Order{ private List items; public boolean isEmptyOrderItem(){ return order.getItems().size() == 0; } public boolean isLessThanOrEqualToZero(){ return order.getTotalPrice() SOLID에 대하여 자기만의 언어로 정리SRP클래스가 변경될 이유는 단 하나다!!클래스 설계 시, 하나의 관심사로 응집하여 구현하도록 하자OCP기존 코드의 변경 없이, 기능 추가하도록 설계가 되어야 한다 다형성을 이용하여 클라이언트 코드의 변경 없이 구현체를 쉽게 변경이 가능하다LSP부모 클래스에서 정의한 스펙을 자식 클래스에서도 지켜야 한다instance of로 타입 체크하여 특정 타입에 대해서만 처리가 필요하다는 건 LSP를 위반한 사례이다!!ISPSRP와 비슷하게 인터페이스 또한 한 가지 책임을 가지도록 분리를 잘해야 한다DIP클래스 참조 시, 추상화 레벨이 높은 인터페이스, 추상클래스, 상위 클래스를 참조하도록 하자코드에 구현체가 드러나게 되면 기능 변경시, 클라이언트 코드의 변경이 생기므로추상화 레벨이 높은 인터페이스 등을 메소드 파라메터, 필드, 리턴타입으로 활용하도록 하자
백엔드
・
ReadableCode
・
워밍업클럽2기