객체지향 프로그래밍으로 테스트코드 작성해보기

BRINCE·2022년 9월 29일
0

스프링 스터디

목록 보기
3/10

서론

내가 지금까지 짜보았던 코드들은 단순히 뭔가 여러군데에 묶여서 한번 뭐가 잘못되고 수정하려면 고쳐야할 코드들이 굉장히 많다는걸 강의를 들으면서 느끼게 되었다..

강사님께서 강의 하실때 매번 강조하셨던게 낮은 결합도, 높은 응집도 였고, 생각할 수 있는 모든것들을 메소드와 객체로 생성해 말그대로 객체지향 프로그래밍을 보여주셨는데, 강의 중에 음식을 주문하는 코드를 작성하는 강의가 있었고, 이 강의를 활용해서 나만의 방식으로 무언가를 짜보았다..

비슷한 예제로 상품을 주문하는 코드를 작성해보았다.

생각해보기

  • 상품을 주문하려면 주문하려는 접속자가 있어야한다.
  • 접속자가 있으려면 접속자를 생성할 생성자가 있어야한다.
  • 생성자가 있고 접속자가 생기면 접속자를 관리하고 정보를 가져올 접속자 리스트가 있어야 한다.
  • 물품을 생산하려면 제조사가 있어야한다.
  • 제조사는 물품생산한다.

이런 방식으로 꼬리에 꼬리를 물어 더 효율적인 객체지향 프로그래밍을 위해 나만의 나무를 만들어간다.

단순히 우리가 물을 마시려면 물을 마신다! 로 끝나는게 아니고 , 이 부분에서는 물을 마시기 위해 사람이 있어야하며, 사람은 입이 있어야하며, 물을 담을 컵이 필요하며 ... 이런 방식으로 생각을 해나가는 것인것 같다.

코드 작성

테스트 코드 작성을 위해 assertj 라이브러리를 불러와주고, 가장 간단한것부터 테스트 코드를 작성 후에 , TDD방식으로 코드를 작성했다.

(정확한건 나도 잘 모르지만, 테스트 코드를 먼저 작성후에, 실패가 나오면 성공을 시키기 위해 점점 무에서 유를 창조하는 방식 정도로 생각했다.)

접속자를 생성할 테스트 클래스인 CustomerTest 클래스를 생성후에 생성자를 호출하는 테스트 코드를 작성했다.

public class CustomerTest {
    @DisplayName("접속자가 생성되는지 테스트한다.")
    @Test
    void createCustomerTest() {
        Customer customer = new Customer("id","pw");
    }

DisplayName 으로 이 메소드가 무엇을 테스트 하는지 표시해줄 수 있다.

처음에 그냥 이렇게 작성을 하면 당연히 오류가 뜬다.

Customer클래스와 Customer 생성자 메소드가 없기 때문이다.

이렇게 오류가 처음에 발생한다면, 오류를 발생하지 않게 코드를 순차적으로 작성해서 프로그램을 완성시켜나가면 된다.

Customer 클래스를 생성하고, 생성자를 선언 후에 테스트를 하면 성공이 뜬다.

이런 방식으로 무에서 유를 창조하는 방식으로 코드를 작성했다.

꼬리물기

접속자

처음으로 Customer 클래스와 생성자를 생성한 후에 ,

Customer 정보가 들어있는 CustomerList 클래스를 생성해, 리스트를 가져오는 테스트 코드를 작성했고,

이거를 활용해 비슷하게 로그인 테스트코드를 만들어보았다.

@DisplayName("아이디와 비밀번호가 맞을때 로그인이 가능한지 테스트합니다.")
    @Test
    void loginTest() {
        Customer customer = new Customer("myid1","1111");
        CustomerList customerList = new CustomerList(customer);
        LoginMode login = new LoginMode("myid1","1111");
        assertThat(Customer.login(login,CustomerList.getCustomers())).isNotEqualTo(false);
    }

이걸 작성하면 또 오류가 난다.
왜냐하면 로그인 모드와 로그인 메서드가 없기 때문이다..

또 이렇게 살을 붙여 로그인 모드 클래스와 내가 입력한 정보를 저장할 생성자 메소드를 선언하고, Customer 클래스에 로그인 메소드를 선언해주고 매개변수로 로그인모드 객체와 CustomerList 를 받아 정보를 검색해주도록 만들어주었다.

public class CustomerList {
    private static List<Customer> customers = new ArrayList<>();


    public CustomerList(Customer customer) {
        customers.add(customer);
    }

    public static List<Customer> getCustomers() {
        return customers;
    }
}
    public static boolean login(LoginMode loginMode, List<Customer> customerList) {
        String inputId = loginMode.getId();
        String inputPw = loginMode.getPw();
        return customerList.stream()
                .anyMatch((s-> s.getId().equals(inputId) && s.getPw().equals(inputPw)));
    }

손님 리스트는 정적리스트로 선언해주었고, 계속 멤버가 추가되기 때문에 final로 초기화하면 안된다. 그리고 매번 객체를 생성할 수 없기 때문에 미리 어레이리스트로 초기화를 한 후에 customer 객체를 생성자 매개변수로 대입하면, 배열에 객체를 추가하는 방식으로 생성자를 만들었다.

그리고 로그인 메소드는 customerList 리스트를 스트림으로 가져와 아이디와 비밀번호가 매치하는 객체가 있으면 true를 리턴하도록 선언했다.

이런 방식으로 로그인 메소드를 만들고, 디포짓 메소드를 선언하여 마찬가지로 물품을 구매할 돈을 입금하도록 구성해주었다.

상품

상품을 만들려면 브랜드가 있어야한다.

마찬가지로 브랜드를 생성하는 생성자 테스트 코드를 작성 후에 살을 붙여나갔다.

그리고 브랜드에서 물품을 생산하는데 단순히 물품만 객체로 생성하면 이건 또 객체지향 프로그램에 맞지 않는 구조인것 같다. 👀

그래서 브랜드 클래스에 빌드 메소드를 선언해주어 마찬가지로 커스토머리스트가 커스토머 객체를 받으면 리스트에 추가하듯이, 빌드 메소드에 프로덕트 객체가 들어오면 프로덕트 리스트에 추가하도록 구성해주었다.

    @DisplayName("물품을 생산합니다.")
    @Test
    void createProduct() {
        Brand brand = new Brand("APPLE");
        brand.build("TV",5000);
    }
    public void build(String name, int price) {
        Product product = new Product(name,price,this.name);
        ProductList productList = new ProductList(product);
    }

이렇게 브랜드가 빌드라는 메서드를 호출해서 매개변수로 생산할 제품의 이름과 가격을 대입하면, 제품명,가격, 해당 브랜드의 이름으로 구성된 객체를 생성하여 프로덕트 리스트에 전달해준다.

주문하기

주문하기도 마찬가지로 커스터머테스트 테스트클래스에 주문하기 테스트코드를 작성해가면서 살을 붙였다.

   @DisplayName("물품을 구매할 수 있는지 테스트합니다")
    @Test
    void buyProductTest() {
        Customer customer = new Customer("id1","1111");
        customer.deposit(5000);
        Brand brand = new Brand("APPLE");
        brand.build("TV",5000);
        customer.buyProduct(customer,"TV");
    }

객체를 생성후에 , 해당 객체에 5000원을 입금했고, 애플이라는 브랜드를 생성 후, 애플에서 5000원짜리 티비를 생산했고 난 그걸 구매하는것이다.

    public String buyProduct(Customer customer,String productname) {
        int price =0;
        price = ProductList.getProductList().stream()
                .filter((p)-> p.getName().equals(productname)).findFirst()
                .get().getPrice();
        int money = customer.getMoney();
        if(price>money){return "0";}
        String message = "구매가 완료되었습니다. 현재 잔액은 "+money+"원 입니다.";
        return message;
    }

해당 메서드는 매개타입으로 커스터머 객체와, 구매를 원하는 제품의 이름을 매개변수로 가져온다.

그리고 메서드 내에서 제품목록을 마찬가지로 스트림으로 가져온후에, 이름이 제품이름과 같은 객체를 가져온 후에 가격을 불러온다.

결론

테스트 코드로 내가 원하는 결과를 확인하고 싶을땐 이렇게 리턴값을 다르게 해서 원하는 방향으로 작동이 되는지 확인해보면 된다.

그냥 단순히 물건 주문하는건데 생각하고 작성해야 할게 이렇게 많다;;;

일주일전의 나였으면 그냥 무작정 우다다 해서 클래스 하나에 다 몰아넣고 테스트 코드도 없어서 오류 하나 발견하면 수정할게 산더미였을듯.

테스트코드 작성하는 강의가 결국엔 주 내용이 테스트코드 작성이 맞긴 했지만,

결국 가장 크게 배워간건 유지보수와 객체지향 프로그래밍이었다.

내가 생각했던것과 달리 더 쪼개고 쪼개야 할것들이 많았었고, 그만큼 기능을 바꾸거나 추가하기 굉장히 수월했다.

앞으로 코딩할때 굉장히 도움될 무언가를 가져온 느낌이라서 기분이 좋다 ㅎ

profile
자스코드훔쳐보는변태

0개의 댓글