Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

부산대 BE_김상해 3주차 과제 (2단계) #354

Open
wants to merge 26 commits into
base: gobad820
Choose a base branch
from

Conversation

gobad820
Copy link

@gobad820 gobad820 commented Jul 12, 2024

Test given-when-then에 맞게 점차 수정해나가겠습니다!

혹시 연관 관계 매핑에서 잘못되거나 수정할 부분 피드백 부탁드립니다!

Copy link

@azjaehyun azjaehyun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

상해님 한주동안 수고 많으셨습니다.

갈수록 실력이 느는게 보이네요 :)

머지를 해드리려고 했으나 충돌이 너무 많이 나서

https://uhgenie7.github.io/docs/dev/git/sync-repository

위 내용 참고해서 다시 PR 주세요~

[ 가이드 참고 ]

본인 포크한 경로로 이동
- git clone https://github.com/gobad820/spring-gift-jpa.git
- cd spring-gift-jpa
[ 아래 명령어 참고 ] 해당 자세한 내용 -> https://uhgenie7.github.io/docs/dev/git/sync-repository
1. git remote add upstream https://github.com/kakao-tech-campus-2nd-step2/spring-gift-jpa.git
2. 포크 받은 본인 주소 (local) : git fetch upstream
3. git checkout [본인아이디] - ex) git checkout gobad820
4. git merge upsteam/gobad820
5. git push origin main
6. git checkout -b main step2-1  << 본인이 개발한 step2 내용 넣기.
7. step2-1 PR 요청.

Comment on lines +15 to +17
@Autowired
private ProductRepository productRepository;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테스트 코드에서는 @mock 을 많이 사용합니다.
build.gradle 아래내용 추가

testImplementation 'org.mockito:mockito-core:4.0.0'
testImplementation 'org.mockito:mockito-junit-jupiter:4.0.0'

테스트 코드도 첨부 드립니다.

package gift.service;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.when;

import gift.model.Member;
import gift.repository.MemberRepository;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mindrot.jbcrypt.BCrypt;

import java.util.Optional;

@ExtendWith(MockitoExtension.class)
public class MemberServiceTest {

    @Mock
    private MemberRepository memberRepository;

    @InjectMocks
    private MemberService memberService;

    private final String secretKey = "verysecretkeyverysecretkeyverysecretkey";

    private Member member;

    @BeforeEach
    void setUp() {
        member = new Member("[email protected]", "mypassword");
        member.setId(1L); // ID를 설정해줍니다
    }

    @Test
    void registerMember() {
        String hashedPassword = BCrypt.hashpw(member.getPassword(), BCrypt.gensalt());
        member.setPassword(hashedPassword);

        when(memberRepository.save(member)).thenReturn(member);

        Optional<String> token = memberService.registerMember(member);

        assertThat(token).isPresent();
        String actualToken = token.get();
        assertThat(Jwts.parserBuilder().setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes())).build().parseClaimsJws(actualToken).getBody().getSubject())
            .isEqualTo(member.getId().toString());
    }

    @Test
    void login() {
        String hashedPassword = BCrypt.hashpw(member.getPassword(), BCrypt.gensalt());
        member.setPassword(hashedPassword);

        when(memberRepository.findByEmail(member.getEmail())).thenReturn(Optional.of(member));

        Optional<String> token = memberService.login(member.getEmail(), "mypassword");

        assertThat(token).isPresent();
        String actualToken = token.get();
        assertThat(Jwts.parserBuilder().setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes())).build().parseClaimsJws(actualToken).getBody().getSubject())
            .isEqualTo(member.getId().toString());
    }

    @Test
    void findById() {
        when(memberRepository.findById(member.getId())).thenReturn(Optional.of(member));

        Optional<Member> foundMember = memberService.findById(member.getId());

        assertTrue(foundMember.isPresent());
        assertThat(foundMember.get().getEmail()).isEqualTo(member.getEmail());
    }
}

위 코드처럼 변경하게 되면

1. @Mock으로 MemberRepository를 모킹하여 실제 데이터베이스 접근 없이 미리 정의된 동작을 수행합니다.
2. @InjectMocks로 MemberService에 모킹된 MemberRepository를 주입합니다.
3. when(...).thenReturn(...)을 사용해 MemberRepository 메서드의 반환 값을 미리 정의합니다.
이렇게 하면 실제 데이터베이스와 상호작용하지 않고 MemberService 기능을 테스트 가능!. 

테스트 코드는 데이터베이스와 상호 작용 없이 로직을 검증을 해야하기 때문에 @Autowired보다는 @mock 을 추천드립니다.

Comment on lines +9 to +37
<!--<h1>Product List</h1>-->


<!--<div class="actions">-->
<!-- <div class="actions">-->
<!-- <a href="product/add" class="btn add-btn"> Add Product</a>-->
<!-- <a href="product/delete/form" class="btn delete-btn">Delete Product</a>-->
<!-- <a href="product/update" class="btn update-btn">Update Product</a>-->
<!-- </div>-->

<!-- <div th:if="${not #lists.isEmpty(productList)}">-->
<!-- <ul class="product-list">-->
<!-- <li th:each="product : ${productList}" class="product-item">-->
<!-- <span th:text="${product.id}" class="product-id">Id</span>-->
<!-- <span th:text="${product.name}" class="product-name">Product Name</span>-->
<!-- <span th:text="${product.price}" class="product-price">Price</span>-->
<!-- <span th:text="${product.imageUrl}" class="product-image">imageUrl</span>-->
<!-- </li>-->
<!-- </ul>-->
<!-- </div>-->
<!-- <div th:if="${#lists.isEmpty(productList)}" class="empty-message">-->
<!-- No products available.-->
<!-- </div>-->
<!-- <div th:if="${resultMessage}" class="result-message">-->
<!-- <p th:text="${resultMessage}"></p>-->
<!-- </div>-->
<!-- <div th:if="${errorMessage}" class="error-message">-->
<!-- <p th:text="${errorMessage}"></p>-->
<!-- </div>-->

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

주석은 깔끔하게 지워줍시다~

</div>
<div>
<label for="price">Price:</label>
<input type="number" id="price" th:field="*{price}" placeholder="Enter product price"/>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기에 11.00 값이 들어간다면?

{"message":"Validation failed","errors":{"price":"Failed to convert property value of type 'java.lang.String' to required type 'java.lang.Integer' for property 'price'; For input string: \"11.00\""}}

오류 발생. 예외처리 추가해줘야겠죠?!

Comment on lines +34 to +36
Map<String, Object> response = new HashMap<>();
response.put("message", "Registration failed");
return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오류가 났을때

console log 부분
List of constraint violations:[
	ConstraintViolationImpl{interpolatedMessage='올바른 형식의 이메일 주소여야 합니다', propertyPath=email, rootBeanClass=class gift.model.Member, messageTemplate='{jakarta.validation.constraints.Email.message}'}
]] with root cause

무조건적인 500 에러보다는 대략적인 오류코드나 메세지를 전달해주는것이 좋아요.

this.memberService = memberService;
}

@PostMapping("/register")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이렇게 로그인 처리해야하는 부분은 다른 개발자를 위해서 샘플로 README.md 파일에 추가해주는게 좋아요.

curl --location 'http://localhost:8080/members/register' \
--header 'Content-Type: application/json' \
--data-raw '{"email":"[email protected]","password":"test"}'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants