【Visual Studio 2026|C言語】単体テストプロジェクトの作り方【MSTest】

Visual Studio 2026でC言語のテストエージェントを使ってみたいけど、上手く動かない…

Visual Studio 2026でC言語のテスト環境を作りたいけど、どうすればいいの?

こんな悩みを抱えている方に向けて、今回自分が実際にVisual Studio 2026でC言語の単体テスト環境を作成してみたので、その手順を画像多めで説明していきます。

結論から言うと、Visual Studio 2026でC言語のテスト環境を作りたい場合、C++でテストフレームワークを作成する必要があります。自分もC++はほぼ扱ったことが無かったので少し苦戦しましたが、AIに頼りまくってなんとか環境構築できました。

なお、テスト環境を構築する際にいくつかフレームワークの種類があるのですが、今回は「Microsoft Unit Testing Framework for C++」(以下、MSTestと略します)というMicrosoft標準のモノを使っています。

この記事で分かること
  • Visual Studio 2026でC言語の単体テスト環境を作る方法
    (フレームワーク:Microsoft Unit Testing Framework for C++)
  • 実際に作成したテスト対象、テストのソースコード
  • テスト環境構築時につまづいたポイント
目次

テスト環境作成の背景

はじめに、今回テスト環境を作成することになった背景について触れておきます。特に興味がない方はこの章はスキップしてもらってもOKです。

背景:Copilotデバッガーエージェントでテスト実行ができなかった

過去の記事(Copilotデバッガーエージェントにバグ修正を依頼してみた)で、Copilotエージェントに「テスト実行」をお願いしたところ空振りが続きました。

原因:単体テストプロジェクトが用意出来ていなかった

原因は、素のC言語コンソールアプリにはTest Explorerで実行できる単体テストプロジェクトが存在しなかったことでした。

今回はその環境を実際に整えます。成績管理システム(第1回記事のコード)を題材に、Visual Studio純正のMSTest(C++用のネイティブ単体テストプロジェクト)で自作関数(calc_average等)をテストできるようにするまでの手順をまとめます。

本記事作成のために参照した情報

Microsoftの公式ドキュメント+GitHub Copilot+AI(Claude)で確認

本記事を作成するにあたり、参考にした情報は以下です。

  • Microsoftの公式ドキュメント「Writing unit tests for C/C++
  • Visual Studio 2026 のテスト対象プロジェクトでGitHub Copilotに相談
  • GitHub Copilotの回答で理解しきれなかったところをAI(Claude)に相談

Microsoftの公式ドキュメントでは基本の流れまでは理解できました。

ただ、このドキュメントはシンプルなHelloWorldプロジェクトが題材だったこともあり、「今回のC言語コンソールアプリを、具体的にどう構成し直せば参照できる形になるのか」という実際の手順がよく分かりませんでした

そこで、GitHub Copilotに相談したり、AI(Claude)に今回どのように環境構築しているかを図解したりしてもらいながら進めていきました。

GitHub Copilotに具体的な手順を聞いてみた

GitHub Copilotに「このC言語プロジェクトに単体テストを追加したい」と相談し、自分のプロジェクト構成に合わせた以下の6ステップの手順を教えてもらいました。

Microsoftの公式ドキュメントと、上記手順を組み合わせて、実際に一つずつ進めていきました。

MSTestを用いたC言語の単体テストプロジェクト作成手順

前提:テスト用のプロジェクト構成変更イメージ

単体テストを実施するため、プロジェクト構成は今回以下のように変更しています。

  • 変更前:上段(グレーの箱)
  • 変更後:下段(青、緑色の箱)

手順①Visual Studioでテストプロジェクトを作成する

  1. ソリューションエクスプローラーでソリューションノードを右クリック
  2. コンテキストメニューから、[追加] > [新しいプロジェクト]を選択
  3. 言語をC++に設定し、検索ボックスに「test」と入力
  4. ネイティブ単体テスト プロジェクト」を選択
  5. 任意のプロジェクト名をつけて「作成」をクリック

⇩作成されたテスト用プロジェクト(UnitTest1)

手順② ヘッダと実装を分離する(student.h / student.c)

元々はmain.cにすべてのソースコードを書いていましたが、以下のように分離しました。
これで「student_grade_test」プロジェクトが、静的ライブラリとして「.lib」ファイルを出力するようになります。

  • main.c:student.h#includeして関数を呼ぶだけ
  • student.h:struct Studentと関数プロトタイプ宣言を置く
  • student.c:実際の処理(calc_average, find_max_index, find_min_index, print_all)を書く

分離後のそれぞれのソースコードは以下です。

main.c

/*
 * 成績管理システム(パターン1: データ埋め込み版)
 * Visual Studio 2026 で動作確認想定
 * プロジェクト: 空のプロジェクト(C++)に .c ファイルとして追加すればOK
 */
#include <stdio.h>
#include "student.h"

#define NUM_STUDENTS 5    /* 学生数 */

int main(void)
{
    /* データはソースコード内で初期化(パターン1) */
    struct Student students[NUM_STUDENTS] = {
        { "Sato",      85 },
        { "Suzuki",    92 },
        { "Takahashi", 58 },
        { "Tanaka",    74 },
        { "Watanabe",  67 }
    };

    int max_i, min_i;

    printf("===== 成績管理システム =====\n\n");

    /* 一覧表示 */
    print_all(students, NUM_STUDENTS);

    /* 統計情報 */
    max_i = find_max_index(students, NUM_STUDENTS);
    min_i = find_min_index(students, NUM_STUDENTS);

    printf("--- 統計情報 ---\n");
    printf("平均点: %.1f 点\n", calc_average(students, NUM_STUDENTS));
    printf("最高点: %d 点 (%s)\n", students[max_i].score, students[max_i].name);
    printf("最低点: %d 点 (%s)\n", students[min_i].score, students[min_i].name);

    return 0;
}

student.h

#ifndef STUDENT_H
#define STUDENT_H

#define MAX_NAME 32

/* 学生1人分のデータ */
struct Student {
    char name[MAX_NAME];
    int  score;
};

/* --- 関数プロトタイプ宣言 --- */
void   print_all(const struct Student s[], int n);
double calc_average(const struct Student s[], int n);
int    find_max_index(const struct Student s[], int n);
int    find_min_index(const struct Student s[], int n);

#endif /* STUDENT_H */

student.c

#include "student.h"
#include <stdio.h>

/* 全員分の成績を一覧表示する */
void print_all(const struct Student s[], int n)
{
    int i;

    printf("--- 成績一覧 ---\n");
    printf("番号  名前            点数\n");
    for (i = 0; i < n; i++) {
        /* %2d: 2桁右詰め, %-15s: 15文字左詰め, %4d: 4桁右詰め */
        printf("%2d    %-15s %4d\n", i + 1, s[i].name, s[i].score);
    }
    printf("\n");
}

/* 平均点を計算して返す(人数0のときは0.0を返す) */
double calc_average(const struct Student s[], int n)
{
    int    i;
    double sum = 0.0;

    if (n <= 0) {
        return 0.0;
    }
    for (i = 0; i < n; i++) {
        sum += s[i].score;
    }
    return sum / n;
}

/* 最高点の学生の添字を返す(同点なら先に見つかった方) */
int find_max_index(const struct Student s[], int n)
{
    int i;
    int max_i = 0;

    for (i = 1; i < n; i++) {
        if (s[i].score > s[max_i].score) {
            max_i = i;
        }
    }
    return max_i;
}

/* 最低点の学生の添字を返す(同点なら先に見つかった方) */
int find_min_index(const struct Student s[], int n)
{
    int i;
    int min_i = 0;

    for (i = 1; i < n; i++) {
        if (s[i].score < s[min_i].score) {
            min_i = i;
        }
    }
    return min_i;
}

分離前のソースコード(main.c)全文は、以下の記事を参照ください。

手順③ソリューション内の他のプロジェクトへの参照を作成する

  1. 手順①で作成したテスト用プロジェクトの「参照」を右クリック→「参照の追加
  2. テスト対象のプロジェクト(今回だと「student_grade_test」)をチェック

手順④student_grade_testを静的ライブラリ化する

  1. テスト対象のプロジェクト(今回だと「student_grade_test」)を右クリック→「プロパティ」をクリック
  2. 「構成プロパティ」→「全般」→「構成の種類」を Static library (.lib) に変更

出力が実行ファイル(.exe)からライブラリ(.lib)に変更されるため、コンソール画面での実行は出来なくなります。テスト環境としては意図通りのため、ご安心ください。

手順⑤インクルードパスを設定する

  1. 「UnitTest1」プロジェクトを右クリック→「プロパティ」
  2. 「C/C++」→「全般」→「追加のインクルードディレクトリ」→プルダウンから「<編集…>」
  3. 「新しい行」→「$(SolutionDir)student_grade_test(またはソースの相対パス)」を追加

手順⑥テストメソッドを記述する

  1. テストプロジェクトファイル(.cpp)にテストコードを記述する

今回は以下のようなテストコードを作成しました。

UnitTestStudentGrade.cppはC++のファイルです。C++はC言語と違い、コンパイル時に関数名を内部的に変換する仕組み(名前修飾)を持っているため、C言語の関数(student.c側)をそのまま宣言しただけだと、C++側とC言語側で関数名の解釈がずれてしまい、ビルド時にリンクエラーになってしまいます。

extern "C" { ... }で囲むことで、「この中の関数はC言語のルールでコンパイルされたものだよ」とコンパイラに伝え、正しくリンクできるようにしています。

UnitTestStudentGrade.cpp

#include "pch.h"
#include "CppUnitTest.h"
using namespace Microsoft::VisualStudio::CppUnitTestFramework;

extern "C" {
    /* main.c と合わせておく(32 は MAX_NAME と整合させる) */
    struct Student { char name[32]; int score; };

    double calc_average(const struct Student s[], int n);
    int find_max_index(const struct Student s[], int n);
    int find_min_index(const struct Student s[], int n);
}

TEST_CLASS(StudentGradeTests)
{
public:

    TEST_METHOD(TestCalcAverage_Normal)
    {
        Student s[] = { {"Sato",85}, {"Suzuki",92}, {"Takahashi",58} };
        double expected = (85.0 + 92.0 + 58.0) / 3.0;
        double actual = calc_average(s, 3);
        Assert::AreEqual(expected, actual, 1e-9, L"平均が正しく計算されること");
    }

    TEST_METHOD(TestCalcAverage_ZeroN)
    {
        Student s[] = { {"X",100} };
        double actual = calc_average(s, 0);
        Assert::AreEqual(0.0, actual, 1e-9, L"n==0 で 0.0 を返すこと");
    }

    TEST_METHOD(TestFindMaxAndMin_Basic)
    {
        Student s[] = {
            {"A", 50},
            {"B", 75},
            {"C", 75}, /* 同点は先に見つかった方を返す点を確認 */
            {"D", 20}
        };

        int maxIndex = find_max_index(s, 4);
        int minIndex = find_min_index(s, 4);

        Assert::AreEqual(1, maxIndex, L"最高点は index 1 (最初の75) であること");
        Assert::AreEqual(3, minIndex, L"最低点は index 3 であること");
    }
};

手順⑦ ビルドしてテストを実行する

  1. Ctrl+Shift+Bでソリューション全体をビルドする
  2. エラーがなければ、「テスト」→「テストエクスプローラー」→「ビュー内のすべてのテストを実行」

つまずいたポイント

テストファイルのインクルード漏れ

テストファイルを作成してビルドした際、以下のエラーが発生しました。

GitHub Copilotに聞いてみたところ、以下のように原因と対処法を教えてくれました。

ということで、以下のインクルードが足りていなかったため、冒頭に1行追加したことでエラー解消しました!

#include "pch.h"

まとめ

公式ドキュメントだけでは自分のプロジェクト構成への当てはめ方が分からず、Copilotに相談しながら進める形になりました。

今後、同じ構成をGoogle Testでも試して比較してみたいと思います。

最後まで読んでいただき、ありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次