bookmark_borderTDD(spring)-Hashed password

  1. Prerequisite
  2. Write third test case for the project
    1. Test Case: Received message and hashed password
    2. Test case들을 추가 (receive message & hashed password)
    3. H2 Database console 설정 in application.yml
    4. GenericResponse.java class 추가
    5. UserService.java class에 encode method 사용
    6. UserController.java class Return값 변경
    7. Test 실행

Prerequisite

post user and saving user to DB

Two Test Cases 추가 (Received message and hashed password )

in test/java/com/example/backend

package com.example.backend;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.List;

import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

import com.example.backend.shared.GenericResponse;
import com.example.backend.user.User;
import com.example.backend.user.UserRepository;

@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
@TestMethodOrder(OrderAnnotation.class)
public class UserControllerTest {

    @Autowired
    TestRestTemplate testRestTemplate;

    @Autowired
    UserRepository userRepository;

    private static final String API_1_0_USERS = "/api/1.0/users";

    private User createValidUser() {
        User user = new User();
        user.setUsername("test-user");
        user.setDisplayName("test-display");
        user.setPassword("Password");
        return user;
    }

    @Test
    @Order(1)
    public void postuser_whenUserIsValid_userSaveToDatabase() {
        User user = createValidUser();
        testRestTemplate.postForEntity(API_1_0_USERS, user, Object.class);
        assertThat(userRepository.count()).isEqualTo(1);
    }

    @Test
    @Order(2)
    public void postuser_whenUserIsValidreceiveOK() {
        User user = createValidUser();
        ResponseEntity<Object> response = testRestTemplate.postForEntity(API_1_0_USERS, user, Object.class);
        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
    }

    @Test
    @Order(3)
    public void postuser_whenUserIsValid_receiveSuccessMessage() {
        User user = createValidUser();
        ResponseEntity<GenericResponse> response = testRestTemplate.postForEntity(API_1_0_USERS, user, GenericResponse.class);
        assertThat(response.getBody().getMessage()).isNotNull();
    }

    @Test
    @Order(4)
    public void postUser_whenUserIsValid_passwordIsHashedInDataBase(){
        User user = createValidUser();
        testRestTemplate.postForEntity(API_1_0_USERS, user, Object.class);
        List<User> users = userRepository.findAll();
        User inDB = users.get(0);
        assertThat(inDB.getPassword()).isNotEqualTo(user.getPassword());            
    }
}
  • Test cases: postuser_whenUserIsValid_receiveSuccessMessage & postUser_whenUserIsValid_passwordIsHashedInDataBase 추가

H2 Database console 설정

spring:
    datasource:
        generate-unique-name: false
    h2:
        console:
            enabled: true
            path: /h2-console
  • application.properties에서 application.yml으로 변경함
  • 실행후 H2 DB사용을 위해 Path를 추가함 http://localhost:8080/h2-console
  • generate-unique-name: false으로 setting하여 JDBC URL변경없이 사용함.

GenericResponse.java class 추가

in java/com/example/backend/shared

package com.example.backend.shared;

import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
public class GenericResponse {
    private String message;

    public GenericResponse(String message){
        this.message = message;
    }
    
}
  • @NoArgsConstructor will generate a constructor with no parameters.

UserService.java class에 encode method 사용

in java/com/example/backend/shared

package com.example.backend.user;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    UserRepository userRepository;

    BCryptPasswordEncoder passwordEncoder;
    
    public UserService(UserRepository userRepository){
        super();
        this.userRepository = userRepository;
        this.passwordEncoder = new BCryptPasswordEncoder();
    }

    public User save(User user){
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        return userRepository.save(user);
    }

}

in UserController.java class Return값 변경

in java/com/example/backend/shared

package com.example.backend.user;

import com.example.backend.shared.GenericResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

    @Autowired
    UserService userService;
    

    @PostMapping("/api/1.0/users")
    public GenericResponse createUser(@RequestBody User user){
        userService.save(user);
        // GenericResponse body = new GenericResponse();
        // body.setMessage("User saved");
        return new GenericResponse("User saved");
    }
}

Test 실행

$ mvn test 

INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  7.980 s
[INFO] Finished at: 2020-09-11T15:26:44+09:00
[INFO] ------------------------------------------------------------------------
ANOTE.DEV