본문 바로가기
개발/TIL

[TIL]TDD스터디_Test 메소드 접근제어자(Public) & Record 클래스 타입

by 반비🥰 2024. 5. 14.
반응형
 

[TIL]JAVA사전스터디_연산자, 조건문, 반복문, 배열, 컬렉션

[TIL]JAVA사전스터디_기본형 변수, 참조형 변수, 형변환📌 자바 개발 환경JRE(Java Runtime Environment): 자바 실행 환경으로 JRE(JVM)만 있다면 Java 프로그램을 실행만 시킬 수 있음: .class파일만 실행 가능 

success-notes.tistory.com

 

 


 

 

해당 포스트는 "실전! 스프링부트 상품 주문 API 개발로 알아보는 TDD" 강의를 수강하여 작성한 글입니다.

 

📌 오늘 얻은 지식


❗️왜 @Test의 접근자는 왜 public이어야 하는가?

현재 Junit는 테스트 클래스의 테스트 메서드를 자바의 reflection api를 통해 호출

➡️ 초기 JUnit이 만들어지는 때 사용했던 JDK 1.1에서는 relfection에서 public 메서드만 허용하여 현재까지 @Test 클래스의 접근제어자가 public이 되었다.

public class serviceTest{

	@Test
    void 상품등록(){
    	// 테스트코드
    }
	
}

 

 

➕ JUnit의 메서드는 모두 public이어야 한다.

       @Test 어노테이션이 붙은 메소드는 접근제어자가 public임

public class ServiceTest{

	@Test
    public void 상품등록_1(){
    	// Test 메소드이기 때문에 접근제어자가 public
    }
    
    private void 상품등록_2(){
    	// Test 메소드가 아니기 때문에 접근제어자가 private이어도 괜찮음
    }
}

 

 

❗️RECORD란?

RECORD란 JAVA 14에서 처음 소개된 클래스 타입으로, 변경 불가(immutable) 데이터 객체를 쉽게 만들 수 있다.

기존의 클래스와 비슷하지만, 더 간결하고 효율적으로 데이터 객체를 생성 가능하도록 설계되었다.

 

1) 기존방법

public class Student{
	
    private String name;
    private int age;
    
    public Student(String name, int age){
    	this.name = name;
        this.age = age;
    }
    
    public String getName(){
    	return name;
    }
    
    public int getAge(){
    	return age;
    }
}

 

2) Record 방법

public record Student(String name, int age){
	// 기존 방법에 비해 간결
}

 

3) 차이점

  기존 방법 Record 방법
차이점 모든 필드에 final 선언
필드값을 모두 포함한 생성자
접근자 메소드
클래스의 상속을 제한하려면 클래스 레벨에도 final 선언
필드 캡슐화
생성자 메소드
접근자 메소드                      ➡️ 자동생성
equals 메소드
hashcode 메소드
toString 메소드 

 

➡️ 컴파일 타임에 컴파일러가 코드를 추가

 

접근자 메서드 호출 

Record 클래스는 키워드를 사용하여 레코드 개체(레코드 클래스의 인스턴스)를 생성

// 기존 방법
Student student1 = new Student("choi",25);
System.out.println(student1.getName());

// Record
Student student2 = new Student("park", 20);
System.out.println(student2.length()); //해당 접근자 메서드 호출

 

 

👩🏻‍💻 작성 코드


test/product/ProductServiceTest.java

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

// 왜 public을 삭제하나?
class ProductServiceTest {

    private ProductService productService;
    private ProductPort productPort;
    private ProductRepository productRepository;

    @BeforeEach
    void setUp(){
        productRepository = new ProductRepository();
        productPort = new ProductAdapter(productRepository);
        productService = new ProductService(productPort);
    }
    @Test
    void 상품등록(){
        final AddProductRequest request = 상품등록요청_생성();
        productService.addProduct(request);
    }

    private AddProductRequest 상품등록요청_생성() {
        final String name = "상품명";
        final int price = 1000;
        final DiscountPolicy discountPolicy = DiscountPolicy.NONE;
        return new AddProductRequest(name, price, discountPolicy);
    }

}

 

 

main/product/AddProductRequest.java

import org.springframework.util.Assert;

record AddProductRequest(String name, int price, DiscountPolicy discountPolicy) {

    public AddProductRequest {
        Assert.hasText(name, "상품명은 필수입니다.");
        Assert.isTrue(price > 0, "상품 가격은 0보다 커야 합니다.");
        Assert.notNull(discountPolicy, "할인 정책은 필수입니다.");
    }
}

 

 

main/product/DiscountPolicy.java

enum DiscountPolicy {
    NONE
}

 

 

main/product/Product.java

import org.springframework.util.Assert;

class Product {

    private final DiscountPolicy discountPolicy;
    private final int price;
    private final String name;
    private Long id;

    public Product(final String name, final int price, final DiscountPolicy discountPolicy) {
        Assert.hasText(name, "상품명은 필수입니다.");
        Assert.isTrue(price > 0, "상품 가격은 0보다 커야 합니다.");
        Assert.notNull(discountPolicy, "할인 정책은 필수입니다.");
        this.name = name;
        this.price = price;
        this.discountPolicy = discountPolicy;
    }

    public Long getId() {
        return id;
    }

    public void assignId(Long id) {
        this.id = id;

    }
}

 

 

main/product/ProductAdapter.java

class ProductAdapter implements ProductPort {

    private final ProductRepository productRepository;

    ProductAdapter(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }

    @Override
    public void save(Product product) {
        productRepository.save(product);
    }
}

 

 

main/product/ProductPort.java

interface ProductPort {
    void save(final Product product);
}

 

 

main/product/ProductRepositort.java

class ProductRepository {
    private Long sequence = 0L;
    private Map<Long, Product> persistence = new HashMap<>();

    public void save(final Product product) {
        product.assignId(++sequence);
        persistence.put(product.getId(), product);
    }
}

 

 

main/product/ProductService.java

class ProductService {

    private final ProductPort productPort;

    ProductService(ProductPort productPort) {
        this.productPort = productPort;
    }

    public void addProduct(AddProductRequest request) {
        final Product product = new Product(request.name(), request.price(), request.discountPolicy());

        productPort.save(product);
    }
}

 

 

🧐 내가 파악한 구조

 

 

 

본 포스팅은 공부를 위해 작성한 것이므로, 틀린 내용이 있을 수 있습니다.  

틀린 내용을 댓글을 통해 알려주시면 수정하도록 하겠습니다:)

📌 참고사이트


@Test 메소드 접근제어자 - https://groups.google.com/g/ksug/c/xpJpy8SCrEE

RECORD - https://docs.oracle.com/en/java/javase/22/language/records.html

반응형