とらすたのーと

試したこと。学んだこと。おぼえがき。

Claude CodeとCursor

最近、AIコーディングツールとして話題となっているのが、CursorClaude Code
両方とも、AIにコードを書かせる・修正させることができますが、どっちを使えばいいのか、どう違うのか、何が出来て何が出来ないのか、詳しく知りたかったので調べてみました。

Cursor と Claude Code の 基本的な違い

Cursorは、VS Codeベースのエディタで、複数モデルを切り替えて、利用できることができます。エディタに統合されたチャットで質問や修正指示をしながらコーディングができます。つまり、AIを活用できるエディタです。
一方、Claude Codeは、CLIベースのAI コーディングエージェントです。ターミナルで指示を出しコーディングします。こちらはエージェントです。

自律性の違い

この2つは、自律性という点で大きな違いがあるようです。「自分で動けるか/人間の指示待ちか」 という点で大きく違います。

  • Cursor

    • 指示されたファイルを修正・生成するが、補助がメイン
    • Git 操作やテスト実行は人間が行う必要あり
    • ユーザーが逐次管理しながら進めるスタイル
  • Claude Code

    • ゴールを伝えると、計画を立ててタスクを実行
    • 複数ファイルを跨ぐ修正や Git 操作も含めて処理
    • 長時間連続して作業を進めることも可能

ユースケース比較表

ChatGPTに比較表を作ってもらいました。

作業内容 Cursor(Claude/GPT/Mistral 切替可) Claude Code(CLI、Claude固定)
小規模な修正(関数のリファクタリング、変数名変更など) ◎ 最適。ファイル内でのインライン補完が速く効率的。 △ 可能だがターミナルでやるよりエディタ内の方が早い。
新しい関数やクラスの追加 ◎ 「ここに関数を追加して」で即座に補完可能。 ○ 実装できるが手間的には Cursor が向く。
複数ファイルにまたがる修正(例:API仕様変更で呼び出し箇所を全部直す) △ できるが、ユーザーがファイルを逐次指定する必要あり。 ◎ 自動で関連ファイルを探して横断的に修正可能。
ライブラリ導入+設定ファイル修正 △ ライブラリ導入はユーザーが手でやる必要あり。 requirements.txtpackage.json を自動更新し、依存解決も提案。
テストの作成・実行・修正 △ テストコードは生成できるが、実行・修正は人間が操作。 ◎ AI がテストを書いて実行 → 失敗箇所を修正 → 再テストまで可能。
Git 操作(ブランチ切替、コミット、PR用の差分生成など) ✕ 基本的にユーザーが操作。 ◎ AI がコミットメッセージ作成や Git 操作まで実行できる。
大規模リファクタリング(フォルダ構成変更、命名規則統一など) △ 人間が進行管理しながら部分的に依頼する必要あり。 ◎ AI が計画を立てて順番にファイルを修正・検証。
新機能の追加(例:ログイン機能を丸ごと追加) △ 可能だが、ユーザーが細かく「ルーティング作って」「DBモデル追加して」と分解する必要あり。 ◎ 「ログイン機能を追加して」と伝えると、設計 → 実装 → テスト → Git コミットまで自律的に実行可能。

まとめ

まとめると、逐次修正指示を出しながら進めていくならCursorを使用し、自律的に動いて貰うことで大きなタスクを任せたい場合はClaude Codeが良いという感じです。
逆に言うと、Claude Codeを使うなら、自律的に動いて貰うように使わないと損だよね(高いお金払ってるし)って感じですね

紙の書類を取り込んで英訳するベストプラクティス

先日、日本語で書かれた紙の資料を海外の方に送りたいことがありました。 いくつか方法を試した結果、思ったより簡単にできる方法を見つけたので書いておこうと思います。

手順は2つ

一気やることは難しいので、まずは、手順を段階分けしてみました。

  1. 紙の書類を取り込む
  2. 英訳する

それぞれやったことを書いていこうと思います。

1. 紙の書類を取り込む方法

今回の資料は Wordで作ったドキュメントを印刷したものでした。
なので、まずはその紙の資料をデジタルデータに変換しようと考えました。つまり、OCR(文字認識)ですね。
方法をいくつか試してみました。

  • 写真を撮ってChatGPTに読み込ませる
    まずは、写真を撮ってChatGPTに読み込ませてみました。
    しかしうまく認識されず、ChatGPTからはGoogleレンズなどを使ってよと返答されました。
  • Google レンズを使って読み込む
    次に、Google レンズを使って読み込んでみました。 結果驚くほど正確に読み込んでくれました。

2. 英訳の方法

次は翻訳です。AIを使うことにしました。
ポイントは、「どのAIで翻訳すると自然な文章になるか」と考えました。
下記の2つのAIで英文を日本語に翻訳し比較してみました。
(日本語を自然な文章にできれば、英語も自然な文章になるだろう、という考えです。)

比較したところ、ChatGPTで生成した日本語のほうが違和感が少なく、自然に感じられました。
ということで、ChatGPTで英訳することにしました。

まとめ

以下の2つの方法で、簡単に英訳することが出来ました。

  • 紙の書類の読み取りGoogle レンズ
  • 英訳 → ChatGPT

実現する方法はいくつかあると思いますが、一番いい方法はどれかって意外とわからないので、覚えておくとどこかで使えるかもしれませんね。

システム開発におけるAI活用を考えてみた

AIを使用して、動くプログラムを作るのことは、簡単にできる世の中になってきました。 では、システム開発全体ではどのようにAIを活用できるのか、ウォーターフォール型開発を例に理想形を考えてみました。 品質を担保しつつどのように効率化できるか、というのが、ポイントになる気がします。

開発工程の分類

まずは、開発の流れを以下のフェーズに分けてみました。

各工程では、インプットをもとに何かしらのアウトプットを作成しています。 AIを活用する場合も、各工程においてそれらのインプットをもとに、アウトプットを作成することになると思います。 そこで、各工程のINPUT資料とOUTPUT資料を洗い出してみました。

粒度感など気にせず書き出してみましたが、洗い出したらキリがない気もしてきたので、いったんここまで。

AI活用のイメージ

次に、これらのINPUT・OUTPUTを、各工程でどのようにAIで活用するのか、イメージをざっと書いてみます。

  • 要件定義
    • 要求定義書をメイン資料、他の資料を補足情報としてAIにINPUTし、要件定義書をOUTPUTしてもらいます。
  • 基本設計
    • 要件定義書をメイン資料、他の資料を補足情報としてAIにINPUTし、基本設計書をOUTPUTしてもらいます。
  • 詳細設計
    • 基本設計をメイン資料、他の資料を補足情報としてAIにINPUTし、詳細設計書をOUTPUTしてもらいます。
  • 実装
    • 詳細設計書をメイン資料、他の資料を補足情報としてAIにINPUTし、プログラムをOUTPUTしてもらいます。
  • コードレビュー
    • AIを利用してコードレビューをします。独自のコードレビュー基準がある場合は、追加観点としてINPUTします。
  • 単体テスト
    • AIを用いてテストケースの自動生成を行い、テストの網羅性を向上させます。
    • 単体テスト仕様書が必要な場合は、AIを用いてテストケースをドキュメントにまとめます。
  • 結合テスト
    • AIを利用してテストシナリオの自動生成を行います。
    • AIを利用して結合テストを実施します。
    • AIを用いてOUTPUT資料を自動生成します。
  • システムテスト
    • AIを用いてテストシナリオの自動生成を行います。
    • AIを利用してシステムテストを実施します。
    • AIを用いてOUTPUT資料を自動生成します。

各工程で、必要なインプットを入れたら求めるアウトプットが出来上がった、ということが実現出来たら、理想的ですね。

まとめ

こんなかんじで、理想的な形を書いてみたんですが、これがどこまで実現できるのか、今後試していけたらなと思います。

システム開発を爆速にできるツールをまとめてみた【2025年7月時点】

近年、様々なAI活用のツールが登場しており、システム開発に活用することで、開発効率や品質を飛躍的に向上させることが出来るようになったと感じています。 そこで、システム開発で活用できる今注目のAIツールについてまとめてみました。使い方を深く理解することで、開発速度爆上がり間違いなしです!

  1. Claude Code

    「Claude Code」は、Anthropic社が提供するターミナル上で動作するAIコーディング支援ツールです。 自然言語での指示に基づいてコードの生成、編集、テスト、Git操作などが出来ます。 ディレクトリ構造やファイル間の関係性を理解し、大規模なプロジェクトでも効率的に作業できます。また、テストコードを作成することも出来ます。 コツとしては、claude.mdを作成、設定することで、プロジェクト固有のルールなどを把握させて、それをもとにコードを作成させることが出来ます。 また、コマンドのオプションにより、一回深く考えさせてからコードの生成を行わせる、ということも可能です。 ただ、費用面では、無料プランが存在しないため、どんなものなのか把握するには、Youtubeなどにユーザーによって多く投稿されている、解説動画などを見るしかないのが現状です。 以上のように様々なことが出来るのですが、GUIではなくターミナル上で動きます。 ですので、ターミナルでの操作に抵抗感があるなら、次に紹介するCursorもおすすめです。

  2. Cursor

    「Cursor」はVS CodeをベースにしたAI特化型のコードエディタです。 こちらもClaude Code同様、自然言語で指示ができ、エディタ上で生成内容、変更内容を確認することが出来ます。 また、コードベース全体を理解したうえで、自然言語から即コード変更やドキュメント更新が可能です。 AIと連携することで開発効率を向上させることができるエディタです。 また、AIモデルを切り替えることが出来るため、指示内容に応じて切り替えるとより良いとのことです。 Cursorは、無料プランが存在しており、多くの機能を試用することが出来ます。 ただ、本格的に開発するには課金したほうが、開発効率が上がると思います。

  3. Kiro

    ここまで紹介した、Claude Codeや、Cursorは、主に、指示して作って動かしてみて、修正指示して作って動かしてみて、を繰り返して、完成に持っていく感じです。 ただ、最初にしっかり設計してからコーディングさせたいと感じる人もいると思います。 それが出来るのが売りなのが、次に紹介するKiroです。 「Kiro」は、AWSが開発したAI IDEです。仕様駆動開発を特徴としており、最初に要件やタスクリストを生成し、それに沿ってコード生成などのタスクを行います。 なので、CursorやClaude Codeでは、プロトタイプの開発という面が多い気がするのですが、 Kiroでは本番開発でも使えるコードが生成できるのではないか、と感じています。 本記事執筆時点では、プレビュー版がリリースされたばかり(2025年7月14日 プレビュー版リリース)なので、これから色々動かして試してみようと思っています。 ちなみに、無料プランもあるようです。

  4. ChatGPT

    そして、あらゆる場面で使えるのが対話型AIチャットです。様々な種類が出ていますが、ChatGPTが一番使いやすいかなと感じています。 ここは私の考えなので、他のものを色々試してみるのもありだと思います。

いくつか挙げてみましたが、いかがでしたでしょうか。 主流になっているのではないか、今後主流になるのではないかというものを挙げてみました。 今後も様々なAI活用ツールが出てくると思いますので、まだまだ新しい情報をキャッチアップしていく必要がありそうな分野だと思います。

AIってどういう仕組みか調べてみた

近年、ChatGPTのような生成系AIや音声アシスタント、画像認識など、AIが身近になってきました。でも、実際にAIがどういう仕組みで動いてるか知らなかったため、ちょっと調べてみました。 認識に間違いがあったらコメントで教えてもらえると嬉しいです。

AI自体について

AIは大きく分けて2つの要素から成り立っているようです。

  • 学習済みモデル:膨大なデータでトレーニングされた「知識のかたまり」
  • 推論エンジン:そのモデルを使って実際に「答えを出す」仕組み

この2つが連携して、入力に対して出力を返す、という仕組みのようです。

AIエージェント

AIを使うために必要となるのがAIエージェントで、ユーザーからの入力を受け取り、学習済みモデルを使って推論を行い、結果を返す役割を果たします。

MCPサーバ

複数のチャネル(LINE、Web、Slackなど)やAIエージェントを統括するバックエンド基盤で、 各エージェントや外部サービスをつなぎ、やり取りを調整・統括するとのことです。

全体の流れは?

ここまでの内容を整理し全体の流れを考えてみました。例として、ChatGPTに質問を投げる場合を考えてみました。

[ユーザー(ChatGPTのUI)]
  ↓
[ChatGPTのMCP]
  ↓
[AIエージェント(GPT-4など)]
  ↓
[推論エンジン + 学習済みモデル]
  ↓
[応答を返す]

これが、内部だけで済むパターンですね。

次に外部のサービスを使う場合も調べてみました。外部のサービスを使う場合は、そのサービスのMCPサーバを使うようです。

たとえば:

  • Figma MCP:デザインデータを構造化してAIに渡す
  • Slack MCP:投稿・通知などをBot経由で受け取り制御
  • Cursor MCP:AIエージェントを選択してプロンプトを整形

などがあるようです。

こちらも流れをまとめてみました。

[ユーザー(ChatGPTのUI)]
    ↓
[ChatGPTのMCPサーバ]
    ↓
[AIエージェント(GPT-4など)]
    ↓
[SlackのMCP(Bot/API統括層)]
    ↓
[Slackのワークスペースに投稿]

SlackのMCPサーバがあるので、それを使うことで、Slackの操作をしているという、流れのようです。

AIにはChatGPTの他にも、GeminiやClaudeなど、いろいろなAIがありますが、これらも同じような仕組みで動いているようです。ただ、推論エンジンと学習済みモデルの違いにより、AIの特性が変わるようです。なので、やりたいことに応じて、AIを選ぶ必要があるようです。

SQLiteを使ったPython+SQLAlchemyのテストコードを書いてみた

概要

前回は、mock-alchemyを使ってテストコードを書いてみました。 今回は、SQLiteを使った場合のテストコードを書いてみようと思います。

やりたいこと

今回のテストも、レポジトリ層の関数に対してのテストとなります。ただ前回と違い、今回はデータベース(SQLite)を使用するので、「データベースがどうなったか」を確認することが出来ます。そこを活かして、今回のテスト内容は以下のイメージです。

  • レポジトリ層の関数を呼び出した時、データベースに対して意図した操作(INSERT、SELECT、UPDATE、DELETEなど)が行われ、データベースが意図したように変化したか。
  • データベース操作の結果が返ってきた時、その結果に基づいて、関数の戻り値が想定通りの内容になっているか。

これらを、Create(作成)、Read(読み取り)、Update(更新)、Delete(削除)の各操作で確認していきます。

事前準備

テスト対象のコードは、前回と同じプログラムです。
前回はこちら

  • models.py
  • repository.py

やってみた

それでは、実際にテストコードを書いていきます。 テストコード用として下記を用意しました。

import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from models import Base, User
from repository import UserRepository
from unittest.mock import MagicMock

@pytest.fixture
def session():
    engine = create_engine("sqlite:///:memory:")
    Base.metadata.create_all(engine)
    TestingSessionLocal = sessionmaker(bind=engine)
    db = TestingSessionLocal()
    yield db
    db.close()

@pytest.fixture
def repo(session):
    return UserRepository(session)

def test_create_user():
    pass

def test_get_user():
    pass

def test_update_user():
    pass

def test_delete_user():
    pass

CREATE

まずはCreate。
テスト内容としては、

  • 関数に対して登録したい値を渡した時、データベースに登録されたか。
  • 関数の戻り値として、登録された結果が呼び出し元に返されるか。

となります。
実際に書いてみたのがこちら。

def test_create_user(repo):
    # given: 
    name = "Taro"
    email = "taro@example.com"
    # when: ユーザーを作成する
    user = repo.create(name=name, email=email)
    # then: ユーザーが正しく作成されていることを確認する
    assert user.id is not None
    assert user.name == name
    assert user.email == email

実際の登録処理を動かし、その結果が想定通りか確認しています。

READ

次はRead。
テスト内容としては、

  • 関数に対して取得したい値(条件)を渡した時、データベースの値を取得したか。
  • 関数の戻り値として、取得された結果が呼び出し元に返されるか。

となります。
実際に書いてみたのがこちら。

def test_get_user(repo):
    # given: 既存のユーザーを作成する
    user = repo.create(name="Jiro", email="jiro@example.com")
    # when: ユーザーIDでユーザーを取得する
    found = repo.get(user.id)
    # then: ユーザーが正しく取得できることを確認する
    assert found is not None
    assert found.name == "Jiro"

初めに登録処理でデータを登録し、そのあとに取得処理を動かすことで、結果が想定通りか確認します。

UPDATE

次はUpdate。
テスト内容としては、

  • 関数に対して更新したい値を渡した時、データベースの値を更新したか。
  • 関数の戻り値として、更新された結果が呼び出し元に返されるか。

となります。
実際に書いてみたのがこちら。

def test_update_user(repo):
    # given: 既存のユーザーを作成する
    user = repo.create(name="Saburo", email="saburo@example.com")
    # when: ユーザー情報を更新する
    updated = repo.update(user.id, name="SaburoUpdated", email="saburo2@example.com")
    # then: ユーザー情報が正しく更新されていることを確認する
    assert updated.name == "SaburoUpdated"
    assert updated.email == "saburo2@example.com"

こちらも初めに登録処理でデータを登録し、そのあとに更新処理を動かすことで、戻り値が想定通りか確認します。

DELETE

次はDelete。
テスト内容としては、

  • 関数に対して削除したい値(条件)を渡した時、データベースのデータを削除したか。
  • 関数の戻り値として、削除された結果が呼び出し元に返されるか。

となります。
実際に書いてみたのがこちら。

def test_delete_user(repo):
    # given: 既存のユーザーを作成する
    user = repo.create(name="Shiro", email="shiro@example.com")
    # when: ユーザーを削除する
    result = repo.delete(user.id)
    # then: ユーザーが削除され、取得できないことを確認する
    assert result is True
    assert repo.get(user.id) is None

こちらも初めに登録処理でデータを登録し、そのあとに削除処理を動かします。
戻り値の確認と、最初に登録したデータを取得してデータがないことを確認しています。

まとめ

前回に比べて、かなり記述量が少なくテストを書くことが出来ました。これは、実際のデータベースを使用することで得られる大きな利点ですね。
ただ、取得、更新、削除のテストでデータ作成の処理を使っているので、作成のロジックがエラーとなったときは、すべてのテストがエラーとなりますね(笑
まぁでも、問題なく動きの確認もできますし、次にテストコードを書く機会があれば、SQLiteを使って書こうと思いました。

pytestでPython+SQLAlchemyのテストコードを書いてみた

概要

PythonとSQLAlchemyを使ってデータベースの操作をしているコードに対して、mock-alchemyを使ってテストコードを書く機会がありました。
その整理と備忘録を兼ねて、まとめようと思います。

やりたいこと

今回のテスト対象となるのは、データベースを操作する関数、つまりレポジトリ層の関数に対してのテストとなります。
テストの内容としては、以下のイメージです。

  • レポジトリ層の関数を呼び出した時、データベースに対して意図した操作(INSERT、SELECT、UPDATE、DELETEなど)が行われようとしているか。
  • データベース操作の結果が返ってきた時、その結果に基づいて、関数の戻り値が想定通りの内容になっているか。

つまり、関数へのインプットとアウトプットの確認となります。
整理して書くとこんな感じですね。

  • 関数呼び出し時のインプット:呼び出し方
  • 関数呼び出し時のアウトプット:データベースに対する操作
  • 関数が結果を返す時のインプット:データベースからの結果
  • 関数が結果を返す時のアウトプット:関数から呼び出し元への戻り値

尚、確認したいのは関数の動きなので、データベース自体の挙動はテストの対象外としてます。

これらを、Create(作成)、Read(読み取り)、Update(更新)、Delete(削除)の各操作で確認していきます。

事前準備

テストコードを書くためには、まずは、テストする元のコードが必要です。
ということで、以下を用意しました。

  • models.py
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String, nullable=False)
    email = Column(String, nullable=False, unique=True)

    def __init__(self, id=None, name=None, email=None):
        self.id = id
        self.name = name
        self.email = email
  • repository.py
from sqlalchemy.orm import Session
from models import User

class UserRepository:
    def __init__(self, db: Session):
        self.db = db

    def create(self, name: str, email: str):
        user = User(name=name, email=email)
        self.db.add(user)
        self.db.commit()
        self.db.refresh(user)
        return user

    def get(self, user_id: int):
        return self.db.query(User).filter(User.id == user_id).first()

    def update(self, user_id: int, name: str = None, email: str = None):
        user = self.get(user_id)
        if not user:
            return None
        if name:
            user.name = name
        if email:
            user.email = email
        self.db.commit()
        self.db.refresh(user)
        return user

    def delete(self, user_id: int):
        user = self.get(user_id)
        if not user:
            return False
        self.db.delete(user)
        self.db.commit()
        return True

今回は、CursorというAI Code Editorを使用して用意してみました。
Cursorを触ってみた感想は、また別のブログ記事にまとめようと思います。

やってみた

それでは、実際にテストコードを書いていきます。 テストコード用として下記を用意しました。

  • test_repository.py
import pytest
import mock
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from models import Base, User
from repository import UserRepository
from unittest.mock import patch, MagicMock
from mock_alchemy.mocking import UnifiedAlchemyMagicMock

def get_session(data=None):
    session = UnifiedAlchemyMagicMock(data=data)
    # commit, refresh, add, delete などをMockで補う
    session.commit = MagicMock()
    session.refresh = MagicMock()
    session.add = MagicMock(side_effect=session.add)
    session.delete = MagicMock(side_effect=session.delete)
    return session

def test_create_user():
    pass

def test_get_user():
    pass

def test_update_user():
    pass

def test_delete_user():
    pass

CREATE

まずはCreate。
テスト内容としては、

  • 関数に対して登録したい値を渡した時、データベースに対してその値を登録しようとするか。
  • 関数の戻り値として、登録された結果が呼び出し元に返されるか。

となります。
実際に書いてみたのがこちら。

def test_create_user():
    # given
    session = get_session([([mock.call.query(User)], [])])
    repo = UserRepository(session)
    # when
    user = repo.create(name="Taro", email="taro@example.com")
    # then
    ## データベースに対してその値を登録しようとするか
    session.add.assert_called_once_with(user)
    called_user = session.add.call_args[0][0]
    assert called_user.name == "Taro"
    assert called_user.email == "taro@example.com"
    session.commit.assert_called_once()
    session.refresh.assert_called_once_with(user)
    ## 登録された結果が呼び出し元に返されるか
    assert user.name == "Taro"
    assert user.email == "taro@example.com"

session.addsession.commitが呼ばれているか、session.addをどんな値で呼んでいるか、を確認することで、どのように登録しようとしているかをテストしています。

READ

次はRead。
テスト内容としては、

  • 関数に対して取得したい値(条件)を渡した時、データベースに対してその値で取得しようとするか。
  • 関数の戻り値として、取得された結果が呼び出し元に返されるか。

となります。
実際に書いてみたのがこちら。

def test_get_user():
    # given: モックのセッションとクエリチェーンを用意
    session = MagicMock()
    mock_query = session.query.return_value
    mock_filter = mock_query.filter.return_value
    mock_filter.first.return_value = User(id=1, name="Jiro", email="jiro@example.com")
    repo = UserRepository(session)
    # when: getを呼び出す
    user = repo.get(1)
    # then
    ## データベースに対してその値で取得しようとするか
    ### query, filter, firstが呼ばれていること
    session.query.assert_called_once_with(User)
    ### filterの条件が正しいことをテストする
    args, kwargs = mock_query.filter.call_args
    assert len(args) == 1
    ### User.id == 1 という比較式になっているか確認
    condition = args[0]
    assert str(condition) == str((User.id == 1))
    mock_filter.first.assert_called_once()
    ## 取得された結果が呼び出し元に返されるか
    assert isinstance(user, User)
    assert user.name == "Jiro"
    assert user.email == "jiro@example.com"

取得対象、条件、取得件数を確認することで、どのように取得しようとしているかをテストしています。

UPDATE

次はUpdate。
テスト内容としては、

  • 関数に対して更新したい値を渡した時、データベースに対してその値で更新しようとするか。
  • 関数の戻り値として、更新された結果が呼び出し元に返されるか。

となります。
実際に書いてみたのがこちら。

def test_update_user():
    # given: モックのセッションとgetの返り値
    session = MagicMock()
    user_obj = User(id=2, name="Saburo", email="saburo@example.com")
    repo = UserRepository(session)
    repo.get = MagicMock(return_value=user_obj)
    # when: updateを呼び出す
    updated = repo.update(2, name="326", email="saburo2@example.com")
    # then
    ## データベースに対してその値で更新しようとするか
    ### user_objの値が更新されていること
    assert user_obj.id == 2
    assert user_obj.name == "326"
    assert user_obj.email == "saburo2@example.com"
    ### commit, refreshが呼ばれていること
    session.commit.assert_called_once()
    session.refresh.assert_called_once_with(user_obj)
    ## 更新された結果が呼び出し元に返されるか。
    assert updated.name == "326"
    assert updated.email == "saburo2@example.com"

どの値でcommitするか確認することで、どのように更新しようとしているかテストしています。

DELETE

次はDelete。
テスト内容としては、

  • 関数に対して削除したい値(条件)を渡した時、データベースに対してその値で削除しようとするか。
  • 関数の戻り値として、削除された結果が呼び出し元に返されるか。

となります。
実際に書いてみたのがこちら。

def test_delete_user():
    # given: getがUserを返すようにモック
    session = MagicMock()
    user_obj = User(id=3, name="Shiro", email="shiro@example.com")
    repo = UserRepository(session)
    repo.get = MagicMock(return_value=user_obj)
    # when: deleteを呼び出す
    result = repo.delete(3)
    # then
    ## データベースに対してその値で削除しようとするか。 
    ### delete, commitが呼ばれていること
    session.delete.assert_called_once_with(user_obj)
    # deleteがどのオブジェクトに対して呼ばれたか(条件)をテスト
    args, kwargs = session.delete.call_args
    assert args[0].id == 3
    session.commit.assert_called_once()
    ## 削除された結果が呼び出し元に返されるか。
    assert result is True

session.deletesession.commitが呼ばれてることを確認することで、どのように削除しようとしているかをテストしています。

まとめ

ひと通り、CRUD操作に対するテストを行うことは出来ました。ただ、この確認観点だと記述量が多くなるなというのが正直な感想です。
今回は、mock-alchemyを使ってみましたが、色々調べてみると、SQLiteを使う方法もあるそうです。
(っていうかそっちのほうが一般的なのかな?)
次回は、SQLiteを使ってテストコードを書く方法を試してみたいと思います。