r잡초처럼
바른 프로그래밍
r잡초처럼
전체 방문자
오늘
어제
  • 분류 전체보기 (124)
    • FastAPI (7)
    • 끄적끄적 (2)
    • Python (17)
    • Django (31)
    • Database (2)
    • Docker (7)
    • 디자인패턴 (2)
    • CS 공부 (12)
      • 알고리즘 (2)
      • 자료 구조 (1)
      • 네트워크 (7)
      • IT 지식 (1)
      • 운영체제 (1)
    • 기타 팁들 (10)
    • Aws (2)
    • 독서 (1)
    • 코딩테스트 공부 (1)
      • 백준 (0)
      • 프로그래머스 (1)
    • DevOps (13)
    • TIL (3)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 5장 회사에서 하는 랜 구성
  • encoding
  • docker
  • 컴퓨터 기본 지식
  • cp949
  • 물리 계층
  • 모두의 네트워크
  • fastapi
  • Batch
  • poetry
  • query param
  • 6장
  • 전기 신호
  • preonboarding
  • 파이썬 클린 코드
  • CS 지식
  • 랜 카드
  • 상속과 컴포지션
  • 랜과 왠
  • 책 리뷰
  • pytest
  • validate
  • 네트워크
  • 케이블의 종류
  • pycharm
  • 상속 안티 패턴
  • 완벽한 IT 인프라 구축을 위한 Docker
  • depends
  • 7장
  • dotenv

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
r잡초처럼

바른 프로그래밍

디자인패턴

저장소 패턴 - 2

2023. 2. 8. 23:55

2023.02.08 - [디자인패턴] - 저장소 패턴을 알아보자 - 1에 이어 두 번째

기능을 구현할 때 항상 테스트부터 작성하자. 이 테스트는 코드(저장소)가 데이터베이스와 제대로 연동하는지 검사하기 때문에 아마 통합 테스트로 분류할 수 있다.

#객체를 저장하는 저장소 테스트(test_repository.py)


def test_repository_can_save_a_batch(session):
    batch = model.Batch('batch1', 'RUSTY-SOAPDISH', 100, eta=None)
    
    repo = repository.SqlAlchemyRepostiory(session)
    repo.add(batch)   # 여기서 테스트하는 메서드는 repo.add() 이다.
    session.commit()   # 저장소 밖에서 .commit()을 반복 수행해야 하며 이를 저장소를 호출하는 쪽의 책임으로 한다. 이런 방식에는 장단점이 있다.
    
    rows = list(session.execute(
   	    'SELECT reference, sku, _purchased_quantity, eta FROM "batches"'  # SQL 문을 사용해 저장된 데이터가 맞는지 검증한다.
    ))
    assert rows == [('batch1', 'RUSTY-SOAPDISH', 100, None)]

다음 테스트는 배치와 할당을 읽어오는 기능을 검사한다. 이 테스트는 좀 더 복잡하다.

# 복잡한 객체를 읽는 기능에 대한 저장소 테스트(test_repository.py)

def insert_order_line(session):
    session.execute(  # 이 테스트는 읽기 기능을 검사한다. 그래서 이 SQL문은 repo.get()이 읽을 데이터를 준비한다.
        "INSERT INTO order_lines (orderid, sku, qty)"
        ' VALUES ("order1", "GENERIC-SOFA", 12)'
    )
    [[orderline_id]] = session.execute(
        "SELECT id FROM order_lines WHERE orderid=:orderid AND sku=:sku",
        dict(orderid="order1", sku="GENERIC-SOFA"),
    )
    return orderline_id 
    

def insert_batch(session, batch_id):
    ...
    

def test_repository_can_retrieve_a_batch_with_allocations(session):
    orderline_id = insert_order_line(session)
    batch1_id = insert_batch(session, "batch1")
    insert_batch(session, "batch2")
    insert_allocation(session, orderline_id, batch1_id)  # 이 부분이 테스트에서 검증이 이루어지는 부분이다. 첫 번째 assrt ==는 타임이 일치하는지 검사하고, 참조가 같은지 검사한다.

    repo = repository.SqlAlchemyRepository(session)
    retrieved = repo.get("batch1")

    expected = model.Batch("batch1", "GENERIC-SOFA", 100, eta=None)
    assert retrieved == expected  # Batch.__eq__ only compares reference
    assert retrieved.sku == expected.sku
    assert retrieved._purchased_quantity == expected._purchased_quantity
    assert retrieved._allocations == {  # 명시적으로 ._allocations 등의 주요 속성이 같은지 검색한다.
        model.OrderLine("order1", "GENERIC-SOFA", 12),
    }

이러한 저장소 패턴은 테스트를 작성할 때 큰 이점을 보여준다. 테스트에서 가짜 저장소를 통해 실제 데이터베이스를 이용하지 않아도 간단한 테스트를 작성할 수 있기 때문이다.

파이썬에서 포트는 무엇이고, 어댑터란 무엇인가

여기서는 주로 의존성 역전에 대해 다룬다. 포트와 어댑터는 객체 지향 세계에서 나온 용어이다. 여기서 포트는 애플리케이션과 추상화하려는 대상 사이의 인터페이스이며, 어댑터는 이 인터페이스나 추상화가 뒤에 있는 구현이라는 정의를 채택한다.

파이썬은 인터페이스라는 요소 자체를 제공하지는 않는다. 따라서 어댑터를 식별하기는 쉽지만 포트를 정의하기는 어렵다. 추상 기반 클래스를 사용한다면 포트다. 추상 기반 클래스를 사용하지 않는다면 포트는 어댑터가 준수하고 애플리케이션이 (모든 어댑터가 지킬 것으로) 기대하는 덕 타입(사용 중인 함수와 메서드 이름과 인수 이름과 타입) 일뿐이다.

구체적으로 여기서는 AbstractRepository는 포트고, SqlAlchemyRepository와 FakeRepository는 어댑터다.

'디자인패턴' 카테고리의 다른 글

저장소 패턴을 알아보자 - 1  (0) 2023.02.08
    '디자인패턴' 카테고리의 다른 글
    • 저장소 패턴을 알아보자 - 1
    r잡초처럼
    r잡초처럼
    오늘보다 내일 더 나은 개발자가 되기 위한 노력을 기록하는 블로그 입니다.

    티스토리툴바