Architecture Patterns with Python (TDD, DDD, EDM)

Overview

architecture-traning

Architecture Patterns with Python (TDD, DDD, EDM)

Chapter 5. 높은 기어비와 낮은 기어비의 TDD

5.2 도메인 계층 테스트를 서비스 계층으로 옮겨야 하는가?

  • 도메인 계층 테스트
def test_prefers_current_stock_batches_to_shipments():
    in_stock_batch = Batch("in_stock_batch", "RETRO-CLOCK", 100, eta=None)
    shipment_batch = Batch("shipment-batch", "RETRO-CLOCK", 100, eta=tomorrow)
    line = OrderLine("oref", "RETRO-CLOCK", 10)
    allocate(line, [in_stock_batch, shipment_batch])

    assert in_stock_batch.available_quantity == 90
    assert shipment_batch.available_quantity == 100
  • 서비스 계층 테스트
def test_prefers_warehouse_batches_to_shipments():
    in_stock_batch = Batch("in-stock-batch", "RETRO-CLOCK", 100, eta=None)
    shipment_batch = Batch("shipment-batch", "RETRO-CLOCK", 100, eta=tomorrow)
    repo = FakeRepository([in_stock_batch, shipment_batch])
    session = FakeSession()
    line = OrderLine('oref', "RETRO-CLOCK", 10)
    services.allocate(line, repo, session)

    assert in_stock_batch.available_quantity == 90
    assert shipment_batch.available_quantity == 100

왜 도메인 계층의 테스트가 아닌 서비스 계층 테스트로 해야할까?

  1. 시스템을 바꾸는 데 어렵지 않다.
  2. 서비스 계층은 시스템을 다양한 방식으로 조정할 수 있는 API를 형성한다.

5.5 서비스 계층 테스트를 도메인으로부터 완전히 분리하기

  • 서비스 테스트에는 도메인 모델에 대한 의존성이 있다. 테스트 데이터를 설정하고 서비스 계층 함수를 호출하기 위해 도메인 객체를 사용하기 때문이다.
  • API를 원시 타입만 사용하도록 다시 작성한다.
# 이전 allocate는 도메인 객체를 받았다.
def allocate(line: OrderLine, repoL AbstractRepository, session) -> str:

# 도메인 의존성을 줄이기 위해 문자열과 정수를 받는다.  -> 원시 타입만 사용!
def allocate(orderid: str, sku: str, qty: int, repo:AbstractRepository, session) -> str:
  • ex) 직접 Batch 객체를 인스턴스화하므로 여전히 도메인에 의존하고 있다. 나중에 Batch 모델의 동작을 변경하면 수많은 테스트를 변경해야하기에 적합하지 않다.
def test_returns_allocation():
    batch = model.Batch("batch1", "Coplicated-lamp", 100, eta=None)
    repo = FakeRepository([batch])
    
    result = services.allocate("o1", "Coplicated-lamp", 10, repo, FakeSession())
    assert result == "batch1"

###5.5.1 위 예시에 대한 해결책 - 마이그레이션: 모든 도메인 의존성을 픽스처 함수에 넣기

  • FakeRepository에 팩토리 함수를 추가하여 추상화를 달성하는 방법 => 도메인 의존성을 한 군데로 모을 수 있다.
class FakeRepository(set):
    @staticmethod
    def for_batch(ref, sku, qty, eta=None):
        return FakeRepository([
            model.Batch(ref, sku, qty, eta)
        ])

    ...
    def test_returns_allocation(self):
        repo = FakeRepository.for_batch("batch1", "Complicated-lamp", 100, eta=None)
        result = services.allocate("o1", "Complicated-lamp", 10, repo, FakeSession())
        
        assert result == "batch1"

###5.5.2 예시 해결책: 누락된 서비스 추가

  • 재고를 추가하는 서비스가 있다면 이 서비스를 사용해 온전히 서비스 계층의 공식적인 유스 케이스만 사용하는 서비스 계층 테스트를 작성할 수 있다.

tip: 일반적으로 서비스 계층 테스트에서 도메인 계층에 있는 요소가 필요하다면 이는 서비스 계층이 완전하지 않다는 사실이다.

def test_add_batch():
    repo, session = FakeSession([]), FakeSession()
    services.add_batch("b1", "Crunchy-armchair", 100, None, repo, session)
    assert repo.get("b1") is not None
    assert session.committed

서비스만 사용하는 서비스 테스트 example code

  • 서비스 계층 테스트가 오직 서비스 계층에만 의존하기 때문에 얼마든지 필요에 따라 모델을 리팩터링할 수 있다.
def test_allocate_returns_allocation():
    repo, session = FakeRepository([]), FakeSession()
    services.add_batch("batch1", "COMPLICATED-LAMP", 100, None, repo, session)
    result = services.allocate("o1", "COMPLICATED-LAMP", 10, repo, session)
    assert result == "batch1"


def test_allocate_errors_for_invalid_sku():
    repo, session = FakeRepository([]), FakeSession()
    services.add_batch("b1", "AREALSKU", 100, None, repo, session)

    with pytest.raises(services.InvalidSku, match="Invalid sku NONEXISTENTSKU"):
        services.allocate("o1", "NONEXISTENTSKU", 10, repo, FakeSession())

5.6 E2E 테스트에 도달할 때까지 계속 개선하기

  • 서비스 함수 덕에 엔드포인트를 추가하는 것이 쉬워졌다 JSON을 약간 조작하고 함수를 한 번 호출하면 된다.
@app.route("/add_batch", methods=['POST'])
def add_batch():
    session = get_session()
    repo = repository.SqlAlchemyRepository(session)
    eta = request.json["eta"]

    if eta is not None:
        eta = datetime.fromisoformat(eta).date()
        
    # JSON 조작 함수 한번 호출
    services.add_batch(
        request.json["ref"],
        request.json["sku"],
        request.json["qty"],
        eta,
        repo,
        session,
    )
    return "OK", 201


@app.route("/allocate", methods=["POST"])
def allocate_endpoint():
    session = get_session()
    repo = repository.SqlAlchemyRepository(session)
    try:
        # JSON 조작 함수 한번 호출
        batchref = services.allocate(
            request.json["orderid"],
            request.json["sku"],
            request.json["qty"],
            repo,
            session,
        )
    except (model.OutOfStock, services.InvalidSku) as e:
        return {"message": str(e)}, 400

    return {"batchref": batchref}, 201

정리: 여러 유형의 테스트를 작성하는 간단한 규칙

  • 특성당 엔드투엔드 테스트를 하나씩 만든다는 목표를 세워야 한다.

    • 예를 들어 이런 테스트는 HTTP API를 사용할 가능성이 높다. 목표는 어떤 특성이 잘 작동하는지 보고 움직이는 모든 부품이 서로 잘 연결되어 움직이는지 살펴보는 것이다.
  • 테스트 대부분은 서비스 계층을 만드는 걸 권한다.

    • 이런 테스트는 커버리지, 실행 시간, 효율 사이를 잘 절충할 수 있게 해준다. 각 테스트는 어떤 기능의 한 경로를 테스트하고 I/O에 가짜 객체를 사용하는 경향이 있다. 이 테스트는 모든 에지 케이스를 다루고, 비즈니스 로직의 모든 입력과 출력을 테스트해볼 수 있는 좋은 장소다.
  • 도메인 모델을 사용하는 핵심 테스트를 적게 작성하고 유지하는 걸 권한다.

    • 이런 테스트는 좀 더 커버리지가 작고(좁은 범위를 테스트), 더 깨지기 쉽다. 하지만 이런 테스트가 제공하는 피드백이 가장 크다. 이런 테스트를 나중에 서비스 계층 기반 테스트로 대신할 수 있다면 테스트를 주저하지 말고 삭제하는 것을 권한다.
  • 오류 처리도 특성으로 취급하자.

    • 이상적인 경우 애플리케이션은 모든 오류가 진입점(예: 플라스크)으로 거슬러 올라와서 처리되는 구조로 되어 있다. 단지 각 기능의 정상 경로만 테스트하고 모든 비정상 경로를 테스트하는 엔드투엔드 테스트를 하나만 유지하면 된다는 의미다(물론 비정상 경로를 테스트하는 단위 테스트가 많이 있어야 한다.).
Owner
minsung sim
Cryptocurrency Quant Trader
minsung sim
Grammar Induction using a Template Tree Approach

Gitta Gitta ("Grammar Induction using a Template Tree Approach") is a method for inducing context-free grammars. It performs particularly well on data

Thomas Winters 36 Nov 15, 2022
Label Hallucination for Few-Shot Classification

Label Hallucination for Few-Shot Classification This repo covers the implementation of the following paper: Label Hallucination for Few-Shot Classific

Yiren Jian 13 Nov 13, 2022
Rax is a Learning-to-Rank library written in JAX

🦖 Rax: Composable Learning to Rank using JAX Rax is a Learning-to-Rank library written in JAX. Rax provides off-the-shelf implementations of ranking

Google 247 Dec 27, 2022
Template repository to build PyTorch projects from source on any version of PyTorch/CUDA/cuDNN.

The Ultimate PyTorch Source-Build Template Translations: 한국어 TL;DR PyTorch built from source can be x4 faster than a naïve PyTorch install. This repos

Joonhyung Lee/이준형 651 Dec 12, 2022
Network Enhancement implementation in pytorch

network_enahncement_pytorch Network Enhancement implementation in pytorch Research paper Network Enhancement: a general method to denoise weighted bio

Yen 1 Nov 12, 2021
Code for ICCV 2021 paper "HuMoR: 3D Human Motion Model for Robust Pose Estimation"

Code for ICCV 2021 paper "HuMoR: 3D Human Motion Model for Robust Pose Estimation"

Davis Rempe 367 Dec 24, 2022
PaddleRobotics is an open-source algorithm library for robots based on Paddle, including open-source parts such as human-robot interaction, complex motion control, environment perception, SLAM positioning, and navigation.

简体中文 | English PaddleRobotics paddleRobotics是基于paddle的机器人开源算法库集,包括人机交互、复杂运动控制、环境感知、slam定位导航等开源算法部分。 人机交互 主动多模交互技术TFVT-HRI 主动多模交互技术是通过视觉、语音、触摸传感器等输入机器人

185 Dec 26, 2022
A scientific and useful toolbox, which contains practical and effective long-tail related tricks with extensive experimental results

Bag of tricks for long-tailed visual recognition with deep convolutional neural networks This repository is the official PyTorch implementation of AAA

Yong-Shun Zhang 181 Dec 28, 2022
End-to-end speech secognition toolkit

End-to-end speech secognition toolkit This is an E2E ASR toolkit modified from Espnet1 (version 0.9.9). This is the official implementation of paper:

Jinchuan Tian 147 Dec 28, 2022
A tutorial on training a DarkNet YOLOv4 model for the CrowdHuman dataset

YOLOv4 CrowdHuman Tutorial This is a tutorial demonstrating how to train a YOLOv4 people detector using Darknet and the CrowdHuman dataset. Table of c

JK Jung 118 Nov 10, 2022
Implementation of "With a Little Help from my Temporal Context: Multimodal Egocentric Action Recognition, BMVC, 2021" in PyTorch

Multimodal Temporal Context Network (MTCN) This repository implements the model proposed in the paper: Evangelos Kazakos, Jaesung Huh, Arsha Nagrani,

Evangelos Kazakos 13 Nov 24, 2022
Social Fabric: Tubelet Compositions for Video Relation Detection

Social-Fabric Social Fabric: Tubelet Compositions for Video Relation Detection This repository contains the code and results for the following paper:

Shuo Chen 7 Aug 09, 2022
KIDA: Knowledge Inheritance in Data Aggregation

KIDA: Knowledge Inheritance in Data Aggregation This project releases our 1st place solution on NeurIPS2021 ML4CO Dual Task. Slide and model weights a

24 Sep 08, 2022
QueryFuzz implements a metamorphic testing approach to test Datalog engines.

Datalog is a popular query language with applications in several domains. Like any complex piece of software, Datalog engines may contain bugs. The mo

34 Sep 10, 2022
📚 A collection of Jupyter notebooks for learning and experimenting with OpenVINO 👓

A collection of ready-to-run Python* notebooks for learning and experimenting with OpenVINO developer tools. The notebooks are meant to provide an introduction to OpenVINO basics and teach developers

OpenVINO Toolkit 840 Jan 03, 2023
Photo-Realistic Single Image Super-Resolution Using a Generative Adversarial Network

Super Resolution Examples We run this script under TensorFlow 2.0 and the TensorLayer2.0+. For TensorLayer 1.4 version, please check release. 🚀 🚀 🚀

TensorLayer Community 2.9k Jan 08, 2023
A pytorch reproduction of { Co-occurrence Feature Learning from Skeleton Data for Action Recognition and Detection with Hierarchical Aggregation }.

A PyTorch Reproduction of HCN Co-occurrence Feature Learning from Skeleton Data for Action Recognition and Detection with Hierarchical Aggregation. Ch

Guyue Hu 210 Dec 31, 2022
PyTorch implementation of paper “Unbiased Scene Graph Generation from Biased Training”

A new codebase for popular Scene Graph Generation methods (2020). Visualization & Scene Graph Extraction on custom images/datasets are provided. It's also a PyTorch implementation of paper “Unbiased

Kaihua Tang 824 Jan 03, 2023
A Real-World Benchmark for Reinforcement Learning based Recommender System

RL4RS: A Real-World Benchmark for Reinforcement Learning based Recommender System RL4RS is a real-world deep reinforcement learning recommender system

121 Dec 01, 2022
A minimal implementation of Gaussian process regression in PyTorch

pytorch-minimal-gaussian-process In search of truth, simplicity is needed. There exist heavy-weighted libraries, but as you know, we need to go bare b

Sangwoong Yoon 38 Nov 25, 2022