0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Claude Code+qwen3.8:27b-mlx(Ollama)でのプログラミング実験

0
Posted at

Claude CodeとローカルLLMでのプログラミング実験

Claude Codeを使えるようにしたので、実験をしてみました。
以前、opencodeやaiderで実験したときは、設定などいじくりまわして対応しましたが、Claude Codeは一旦素のままで対応できたようです。

環境

Ollamaサーバ:MacBookProM5 メモリ32GB
Claude Code実行環境:ubuntu 26.04 LTS

実験

export CLAUDE_CODE_MAX_CONTEXT_TOKENS=12288
claude --model qwen3.8-27b:custom
  • プロンプト
引数を2つ与えて、その引数によって四則演算した結果を全て(4種)表示するJavaソースを作成し、
あわせてJunitのソースコードを作成してカバレッジ100%になるまでテストをしてください。
なお、0除算の場合は「0除算エラー」の結果を表示してください
ソースコードはjavadoc形式で日本語でコメントを記載してください
  • 結果画面

image.png

image.png

  • テストカバレッジ結果(抜粋)

image.png

image.png

image.png

  • 生成したソースコード
/**
 * 2つの数値を受け取り、四則演算(足し算・引き算・掛け算・割り算)の結果を
 * すべて表示するプログラム。
 * <p>
 * 割り算において除数が 0 の場合は、結果として「0除算エラー」を表示する。
 * </p>
 *
 * <h2>使い方</h2>
 * <pre>
 *   javac Calculator.java
 *   java Calculator &lt;数値1&gt; &lt;数値2&gt;
 * 例: java Calculator 10 3
 * </pre>
 *
 * @author shinichi
 */
public class Calculator {

    /**
     * 0除算が発生した際に表示するメッセージ。
     * <p>先頭の「0」は全角のゼロである。</p>
     */
    private static final String DIVIDE_BY_ZERO_MESSAGE = "0除算エラー";

    /**
     * このクラスはユーティリティクラスのため、他パッケージからのインスタンス化を禁止する。
     * <p>
     * パッケージ内(テスト)からカバレッジ測定のために呼び出せるよう、
     * アクセス修飾子は {@code private} ではなくデフォルト(パッケージプライベート)とする。
     * </p>
     */
    Calculator() {
        throw new UnsupportedOperationException("インスタンス化できません");
    }

    /**
     * 足し算の結果を表示用の文字列として返す。
     *
     * @param a 第1引数(被加数)
     * @param b 第2引数(加数)
     * @return 「a + b = 結果」の形式の文字列
     */
    public static String add(double a, double b) {
        return format(a) + " + " + format(b) + " = " + format(a + b);
    }

    /**
     * 引き算の結果を表示用の文字列として返す。
     *
     * @param a 第1引数(被減数)
     * @param b 第2引数(減数)
     * @return 「a - b = 結果」の形式の文字列
     */
    public static String subtract(double a, double b) {
        return format(a) + " - " + format(b) + " = " + format(a - b);
    }

    /**
     * 掛け算の結果を表示用の文字列として返す。
     *
     * @param a 第1引数(乗数)
     * @param b 第2引数(被乗数)
     * @return 「a × b = 結果」の形式の文字列
     */
    public static String multiply(double a, double b) {
        return format(a) + " × " + format(b) + " = " + format(a * b);
    }

    /**
     * 割り算の結果を表示用の文字列として返す。
     * <p>
     * 除数 {@code b} が 0 の場合は計算を行わず、結果部分に「0除算エラー」を
     * 含めた文字列を返す。
     * </p>
     *
     * @param a 第1引数(被除数)
     * @param b 第2引数(除数)。0 の場合は0除算エラーとなる
     * @return 「a / b = 結果」の形式の文字列。0除算の場合は「a / b = 0除算エラー」
     */
    public static String divide(double a, double b) {
        if (b == 0.0) {
            return format(a) + " / " + format(b) + " = " + DIVIDE_BY_ZERO_MESSAGE;
        }
        return format(a) + " / " + format(b) + " = " + format(a / b);
    }

    /**
     * 四則演算の全4種(足し算・引き算・掛け算・割り算)の結果を、
     * 改行で区切った文字列としてまとめて返す。
     *
     * @param a 第1引数
     * @param b 第2引数
     * @return 4つの演算結果を改行で連結した文字列
     */
    public static String calculateAll(double a, double b) {
        StringBuilder sb = new StringBuilder();
        sb.append(add(a, b)).append(System.lineSeparator());
        sb.append(subtract(a, b)).append(System.lineSeparator());
        sb.append(multiply(a, b)).append(System.lineSeparator());
        sb.append(divide(a, b));
        return sb.toString();
    }

    /**
     * 引数配列から2つの数値を読み取り、四則演算の結果を文字列として返す。
     * <p>
     * テスト可能性のため、{@link #main(String[])} から分離して用意している。
     * </p>
     *
     * @param args コマンドライン引数(2つの数値の文字列)
     * @return 演算結果またはエラーメッセージを表す文字列
     */
    public static String run(String[] args) {
        if (args.length != 2) {
            return "エラー:2つの数値を引数として指定してください。";
        }
        try {
            double a = Double.parseDouble(args[0]);
            double b = Double.parseDouble(args[1]);
            return calculateAll(a, b);
        } catch (NumberFormatException e) {
            return "エラー:引数は数値で指定してください。";
        }
    }

    /**
     * プログラムの入口。コマンドラインから2つの数値を受け取り、
     * 四則演算の結果を標準出力に表示する。
     *
     * @param args コマンドライン引数(2つの数値)
     */
    public static void main(String[] args) {
        System.out.println(run(args));
    }

    /**
     * 数値を表示用の文字列に変換する。
     * <p>
     * 整数値の場合は小数点以下を省略し(例: {@code 8.0} → {@code "8"})、
     * それ以外の場合はそのままの形式で返す。
     * </p>
     *
     * @param v 変換する数値
     * @return 表示用の文字列
     */
    private static String format(double v) {
        if (v == Math.floor(v)) {
            return Long.toString((long) v);
        }
        return Double.toString(v);
    }
}
  • 生成したテストコード
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;

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

/**
 * {@link Calculator} のユニットテスト。
 * <p>
 * 四則演算の各メソッド、0除算時のメッセージ、引数エラー時の挙動、
 * および {@code main} メソッドまでを網羅し、カバレッジ100%を目指す。
 * </p>
 *
 * @author shinichi
 */
class CalculatorTest {

    /** 標準出力の元々のストリーム(テスト後に復元するため保持)。 */
    private PrintStream originalOut;

    /**
     * テスト実行前に、元の {@code System.out} を保存する。
     */
    @BeforeEach
    void setUp() {
        originalOut = System.out;
    }

    /**
     * テスト実行後に、{@code System.out} を元に戻す。
     */
    @AfterEach
    void tearDown() {
        System.setOut(originalOut);
    }

    // ------------------------------------------------------------------
    // 足し算・引き算・掛け算
    // ------------------------------------------------------------------

    /**
     * 足し算の結果が正しい形式で返されることを確認する。
     */
    @Test
    @DisplayName("add: 整数の足し算")
    void add() {
        assertEquals("3 + 4 = 7", Calculator.add(3, 4));
    }

    /**
     * 引き算の結果が正しい形式で返されることを確認する。
     */
    @Test
    @DisplayName("subtract: 整数の引き算")
    void subtract() {
        assertEquals("10 - 3 = 7", Calculator.subtract(10, 3));
    }

    /**
     * 掛け算の結果が正しい形式で返されることを確認する。
     */
    @Test
    @DisplayName("multiply: 整数の掛け算")
    void multiply() {
        assertEquals("2 × 5 = 10", Calculator.multiply(2, 5));
    }

    // ------------------------------------------------------------------
    // 割り算(0除算を含む)
    // ------------------------------------------------------------------

    /**
     * 除数が 0 でない場合の割り算の結果を確認する。
     */
    @Test
    @DisplayName("divide: 通常の割り算")
    void divideNormal() {
        assertEquals("10 / 2 = 5", Calculator.divide(10, 2));
    }

    /**
     * 除数が 0 の場合、「0除算エラー」が返されることを確認する。
     */
    @Test
    @DisplayName("divide: 0除算")
    void divideByZero() {
        assertEquals("10 / 0 = 0除算エラー", Calculator.divide(10, 0));
    }

    /**
     * 割り算の結果が小数になる場合、整数化されずそのまま表示されることを確認する。
     */
    @Test
    @DisplayName("divide: 小数になる割り算")
    void divideFraction() {
        String result = Calculator.divide(10, 3);
        assertTrue(result.startsWith("10 / 3 ="), "先頭が「10 / 3 =」であるべき");
        assertFalse(result.contains("0除算エラー"), "0除算エラーを含んではいけない");
    }

    // ------------------------------------------------------------------
    // calculateAll(全4種)
    // ------------------------------------------------------------------

    /**
     * 四則演算の全4種が改行で区切られて返されることを確認する。
     */
    @Test
    @DisplayName("calculateAll: 通常の計算")
    void calculateAllNormal() {
        String result = Calculator.calculateAll(10, 3);
        assertTrue(result.contains("10 + 3 = 13"), "足し算の結果を含むべき");
        assertTrue(result.contains("10 - 3 = 7"), "引き算の結果を含むべき");
        assertTrue(result.contains("10 × 3 = 30"), "掛け算の結果を含むべき");
        assertTrue(result.contains("10 / 3 ="), "割り算の結果を含むべき");
    }

    /**
     * 除数が 0 の場合、calculateAll でも割り算の行が「0除算エラー」になることを確認する。
     */
    @Test
    @DisplayName("calculateAll: 0除算")
    void calculateAllDivideByZero() {
        String result = Calculator.calculateAll(10, 0);
        assertTrue(result.contains("10 + 0 = 10"), "足し算の結果を含むべき");
        assertTrue(result.contains("10 / 0 = 0除算エラー"), "割り算が0除算エラーであるべき");
    }

    // ------------------------------------------------------------------
    // run(引数処理)
    // ------------------------------------------------------------------

    /**
     * 2つの数値を正しく受け取った場合、四則演算の結果が返されることを確認する。
     */
    @Test
    @DisplayName("run: 正しい引数の場合")
    void runValid() {
        String result = Calculator.run(new String[] {"10", "3"});
        assertTrue(result.contains("10 + 3 = 13"), "足し算の結果を含むべき");
        assertTrue(result.contains("10 / 3 ="), "割り算の結果を含むべき");
    }

    /**
     * 引数が2つでない場合、エラーメッセージが返されることを確認する。
     */
    @Test
    @DisplayName("run: 引数の個数エラー")
    void runWrongArgCount() {
        assertEquals("エラー:2つの数値を引数として指定してください。",
                Calculator.run(new String[] {"1"}));
    }

    /**
     * 引数が数値でない場合、エラーメッセージが返されることを確認する。
     */
    @Test
    @DisplayName("run: 非数値の引数の場合")
    void runNonNumeric() {
        assertEquals("エラー:引数は数値で指定してください。",
                Calculator.run(new String[] {"abc", "3"}));
    }

    // ------------------------------------------------------------------
    // main(標準出力)
    // ------------------------------------------------------------------

    /**
     * {@code main} が四則演算の結果を標準出力に表示することを確認する。
     */
    @Test
    @DisplayName("main: 結果が標準出力に表示される")
    void mainPrintsResult() throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        System.setOut(new PrintStream(baos, true, StandardCharsets.UTF_8.name()));

        Calculator.main(new String[] {"1", "2"});

        String output = baos.toString(StandardCharsets.UTF_8.name());
        assertTrue(output.contains("1 + 2 = 3"), "足し算の結果が出力されるべき");
    }

    // ------------------------------------------------------------------
    // コンストラクタ(インスタンス化禁止)
    // ------------------------------------------------------------------

    /**
     * プライベートなコンストラクタが例外を投げることを確認する。
     */
    @Test
    @DisplayName("コンストラクタ: インスタンス化できない")
    void constructorThrows() {
        assertThrows(UnsupportedOperationException.class, () -> new Calculator());
    }
}

  • pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--
  Maven ビルド定義。
  - JUnit 5 (Jupiter) でユニットテストを実行する。
  - JaCoCo でカバレッジを測定し、LINE / BRANCH いずれも 100% を強制する。

  実行方法:
    mvn test            # テスト実行 + カバレッジ報告生成 (target/site/jacoco/index.html)
    mvn jacoco:check    # カバレッジが 100% でなければビルドを失敗させる
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>jp.example</groupId>
    <artifactId>calculator</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <properties>
        <!-- 広範な互換性のため Java 8 を基準とする -->
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

        <!-- バージョンの固定 -->
        <junit.jupiter.version>5.10.2</junit.jupiter.version>
        <surefire.version>3.2.5</surefire.version>
        <jacoco.version>0.8.12</jacoco.version>
    </properties>

    <dependencies>
        <!-- JUnit 5 -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>${junit.jupiter.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- JUnit 5 を認識させるため Surefire を明示 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${surefire.version}</version>
            </plugin>

            <!-- カバレッジ測定・100% 強制 -->
            <plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
                <version>${jacoco.version}</version>
                <executions>
                    <execution>
                        <id>prepare-agent</id>
                        <goals>
                            <goal>prepare-agent</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>report</id>
                        <phase>test</phase>
                        <goals>
                            <goal>report</goal>
                        </goals>
                    </execution>
                    <!-- LINE / BRANCH いずれも 100% でなければ失敗 -->
                    <execution>
                        <id>check-coverage</id>
                        <phase>test</phase>
                        <goals>
                            <goal>check</goal>
                        </goals>
                        <configuration>
                            <rules>
                                <rule>
                                    <element>BUNDLE</element>
                                    <limits>
                                        <limit>
                                            <counter>LINE</counter>
                                            <value>COVEREDRATIO</value>
                                            <minimum>1.00</minimum>
                                        </limit>
                                        <limit>
                                            <counter>BRANCH</counter>
                                            <value>COVEREDRATIO</value>
                                            <minimum>1.00</minimum>
                                        </limit>
                                    </limits>
                                </rule>
                            </rules>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?