Develope Story

2019-09-07 [Spring-study] ATDD 1단계

|

ATDD 1단계

  • 요구사항
    • loginAcceptanceTest 구현
    • userAcceptanceTest 통과하기위한 구현
    • loginAcceptanceTest 통과하기위한 구현
  • 공부한 내용

      public HtmlFormDataBuilder addParameter(String key, Object value) {
            this.params.add(key, value);
            return this;
        }
    
    • this 호출후 메소드 적용 → this 리턴

      .addParameter(“_method”, “put”) .addParameter(“password”, “test”) .addParameter(“name”, “자바지기2”) .addParameter(“email”, “javajigi@slipp.net”)

    메소드를 이어 붙이게 해줌

    ### ATDD

    기존의 TDD의 기능을 기능 테스트 수준까지 확장한 것

    https://javacan.tistory.com/entry/TDD-ATDD

    기존 TDD

    • test 작성
    • 실패
    • 구현
    • 리팩토링
    • 외부와의 의존관계가 없기 때문에 한계

    ⇒ 기능 통합시 문제점 발생 ⇒ ATDD 등장

    • 전구간에 대한 테스트 : 시스템이 동작하는 지 테스트
      1. 인수테스트 작성
      2. 실패
      3. 단위테스트
      4. 실패
      5. 단위 구현
      6. 통과 (반복)
      7. 인수 통과..(반복)
      8. 리팩토링

    ### Layered architecture 에서 테스트

    • 시간이 충분하다면 모든 부분을 테스트하는 것이 좋다. 하지만 현실은 그렇지 않다. 어느 부분에 우선순위를 두고 테스트하는 것이 바람직할까?

      controller → service → repository

    ### Basic Auth

    사용이유

    • 로그인 테스트를 위한 테스트는 2번씩 이루어짐
      1. Session ID를 발급받기 위한 테스트를 위한 요청
      2. 테스트할 기능에 대한 요청
    • session을 꺼내서 → 기능 테스트

    이런 단점을 보완하기 위해서 BasicAuth를 활용

    클라이턴트: 아이디 + 비밀번호 ⇒ 인코딩 ⇒ 서버전송

    서버 : Basic 데이터 확인 → 로그인

    • Controller 실행전에 로그인 처리

    HandlerMethodArgumentResolver를 활용한 로그인 사용자 정보 추출

    • @LoginUser 어노테이션 사용해서 로그인 되어있는지 확인

    • 설정 - nextstep참고

        public class BasicAuthInterceptor extends HandlerInterceptorAdapter {
              
          private static final Logger log = LoggerFactory.getLogger(BasicAuthInterceptor.class);
              
          @Autowired
          private UserService userService;
              
          public BasicAuthInterceptor() { }
              
          public BasicAuthInterceptor(UserService userService) {
            this.userService = userService;
          }
              
          @Override
          public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
              throws Exception {
        	 String authorization = request.getHeader("Authorization");
            log.debug("Authorization : {}", authorization);
            if (authorization == null || !authorization.startsWith("Basic")) {
              return true;
            }
              
        //디코더 하는 부분
            String base64Credentials = authorization.substring("Basic".length()).trim();
            String credentials = new String(Base64.getDecoder().decode(base64Credentials),
                Charset.forName("UTF-8"));
            final String[] values = credentials.split(":", 2);
            log.debug("username : {}", values[0]);
            log.debug("password : {}", values[1]);
             try {
          User user = userService.login(values[0], values[1]);
          log.debug("Login Success : {}", user);
          request.getSession().setAttribute(HttpSessionUtils.USER_SESSION_KEY, user);
          return true;
        } catch (UnAuthenticationException e) {
          return true;
             }
          }
        }
      

    ### Mock

    • @mock - 목객체생성
    • db같은 외부 api를 테스트 할 때 사용

    참고 : https://jdm.kr/blog/222

    ### RestRespone

    RestStatus : 상태 객체

      public class RestStatus {
          private boolean status;
        
          public RestStatus(boolean status) {
              this.status = status;
          }
        
          public boolean isStatus() {
              return status;
          }
        
          @Override
          public String toString() {
              return "RestResponse [status=" + status + "]";
          }
      }
    

    RestResponse 객체 : 상태객체 상속하고 전달할 객체

      public class RestResponse extends RestStatus {
          private Map<String, Object> result;
        
          public RestResponse() {
              super(true);
              result = new HashMap<>();
          }
        
          public void addAttribute(String key, Object value) {
              result.put(key, value);
          }
        
          public Map<String, Object> getResult() {
              return result;
          }
      }
    

    ### @Lob

    JPA 어노테이션 참고 : https://m.blog.naver.com/dimigozzang/220510288775

      public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
              NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
              User user = HttpSessionUtils.getUserFromSession(webRequest);
              if (!user.isGuestUser()) {
                  return user;
              }
        
              LoginUser loginUser = parameter.getParameterAnnotation(LoginUser.class);
              if (loginUser.required()) {
                  throw new UnAuthorizedException("You're required Login!");
              }
              return user;
          }
        
      public static final GuestUser GUEST_USER = new GuestUser();
    
    • 세션 코드

        public static User getUserFromSession(NativeWebRequest webRequest) {
                if (!isLoginUser(webRequest)) {
                    return User.GUEST_USER;
                }
                return (User) webRequest.getAttribute(USER_SESSION_KEY, WebRequest.SCOPE_SESSION);
            }
      
    • 세션 → 로그인 체크 → 로그인 되어있지 않을 경우 null이 아닌 guestUser 객체 반환

    @Transaction

    ### 트랜잭션

    • 원자성 (Atomicity)
    • 일관성 (Consistency)
    • 독립성 (Isolation)
    • 지속성 (Durability)

    수정할때 데이터를 꺼내면 수정한 데이터와 꺼내온 데이터가 다름 → 이런것들을 방지하기위함

    • 자세한 내용 구글링 하면 많이 나옴

        @OneToMany(mappedBy="question")
        @Where(clause = "deleted = false")
        @OrderBy("id ASC")
        private List<Answer> answers;
      
        where
            (
                answers0_.deleted = 0
            )
            and answers0_.question_id=?
        order by
            answers0_.id asc
      

    asc 오름차순

    where

      where
              (
                  answers0_.deleted = 0
              )
    

    Interface 활용

      @Override
        public String generateUrl() {
          return String.format("/questions/%d", getId());
        }
    
  • 리뷰
    • tab, space 조정 → diff 최소화
      • 들여쓰기 2칸 → 4칸 조정
    • 객체가 비어있을 땐 EntityNotFoundException사용
    • equals 재정의
    • resource 사용자제

Comments