2019-09-07 [Spring-study] ATDD 1단계
07 Sep 2019 | java spring atddATDD 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 등장
- 전구간에 대한 테스트 : 시스템이 동작하는 지 테스트
- 인수테스트 작성
- 실패
- 단위테스트
- 실패
- 단위 구현
- 통과 (반복)
- 인수 통과..(반복)
- 리팩토링
### Layered architecture 에서 테스트
-
시간이 충분하다면 모든 부분을 테스트하는 것이 좋다. 하지만 현실은 그렇지 않다. 어느 부분에 우선순위를 두고 테스트하는 것이 바람직할까?
controller → service → repository
### Basic Auth
사용이유
- 로그인 테스트를 위한 테스트는 2번씩 이루어짐
- Session ID를 발급받기 위한 테스트를 위한 요청
- 테스트할 기능에 대한 요청
- 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를 테스트 할 때 사용
### 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 사용자제
- tab, space 조정 → diff 최소화