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形式で日本語でコメントを記載してください
- 結果画面
- テストカバレッジ結果(抜粋)
- 生成したソースコード
/**
* 2つの数値を受け取り、四則演算(足し算・引き算・掛け算・割り算)の結果を
* すべて表示するプログラム。
* <p>
* 割り算において除数が 0 の場合は、結果として「0除算エラー」を表示する。
* </p>
*
* <h2>使い方</h2>
* <pre>
* javac Calculator.java
* java Calculator <数値1> <数値2>
* 例: 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>




