2
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?

Windows Forms保守開発におけるE2Eテスト導入事例

Last updated at Posted at 2025-10-04

背景と課題

現在、Windows Formsの保守開発を担当していますが、小さな変更でも予期せぬ影響範囲が大きく、作業に多くの時間を要している状況です。
「レガシーコード改善ガイド」を参考に、テストコードの導入による品質向上と開発効率化を検討し、パイロットプロジェクトとして実装してみましたので、その知見を共有します。

開発環境の制約

長期運用されているシステム特有の課題として、以下のような古い開発環境での動作が必須条件となっています:

OS: Windows 2000、Windows 7など
IDE: Visual Studio 2008~2010など
.NET Framework: 比較的古いバージョン

この制約により、以下の最新ツールは採用できませんでした:

WinAppDriver: Windows 10以降が必要
Playwright: .NET Core/.NET 5+が必要
TestContainers: 環境構築の複雑さと依存関係の問題

TestStack.White採用の理由

古い.NET Frameworkバージョン(.NET 3.5以降)をサポート
既存の開発環境で追加インストールなしで動作可能
UI Automationベースで安定した動作
学習コストが比較的低い

以下の制約は認識していますが、現在の業務要件では問題ありません:

プロジェクトの更新が停止している(最終更新: 2016年頃)
クロスプラットフォーム対応がない(Windows専用)
一部の最新UI要素への対応が不完全

構成

名称 説明
Form1 テスト対象の画面
UnitTest1.vb E2Eのテストコード
testdata.csv テストデータ

開発環境

古い環境でもうごくかは別途確認します。

名称 バージョン 説明
Windows 10 OS
VisualStudio Community 2022 IDE
.Net FrameWork 4.8 WindowsFormのフレームワーク

NuGet関係のパッケージ

名称 バージョン 説明
xUnit 2.9.3 C#のユニットテストのフレームワーク
TestStack.White 0.13.3 UI自動化のフレームワーク

成果物

プロダクトプロジェクト

フォーム読み込み時に、SQLを発行して、テーブルの件数を取得する機能です。

画面

image.png

コード

Form1.vb
Imports System.Data.SqlClient

Public Class Form1
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        TextBox1.Text = "HelloWorld!"
    End Sub
    Private connectionString As String = "Server=127.0.0.1,1432;Database=master;User Id=sa;Password=user@12345;"

    Public ReadOnly Property GetFolderPath As String
        Get
            Return Environment.CurrentDirectory
        End Get
    End Property

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Using conn As New SqlConnection(connectionString)
            conn.Open()
            Dim countSql As String = "SELECT COUNT(*) FROM dbo.TestUsers;"

            Using cmd As New SqlCommand(countSql, conn)
                CountText.Text = CInt(cmd.ExecuteScalar())
            End Using
        End Using
    End Sub
End Class

DDL

CREATE TABLE TestUsers (
    Id INT PRIMARY KEY IDENTITY(1,1),
    Name NVARCHAR(100) NOT NULL,
    Email NVARCHAR(100) NOT NULL,
    Age INT,
    CreatedAt DATETIME DEFAULT GETDATE()
);

テストプロジェクト

testdata.csv

testdata.csv
Name,Email,Age
山田太郎,yamada@example.com,30
佐藤花子,sato@example.com,25
田中一郎,tanaka@example.com,35




テストコード

UnitTest1.vb
Imports System
Imports System.Threading
Imports TestStack.White
Imports TestStack.White.UIItems
Imports TestStack.White.UIItems.WindowItems
Imports TestStack.White.UIItems.Finders
Imports Xunit
Imports TestStack.White.Factory
Imports VBWinform
Imports System.IO
Imports System.Data.SqlClient


Public Class NotepadTestsAdvanced
    Implements IDisposable

    Private application As Application
    Private window As Window


    Private Shared ReadOnly testProjectName As String = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name
    Private Shared ReadOnly solutionPath As String = AppContext.BaseDirectory.Substring(0, AppContext.BaseDirectory.IndexOf(testProjectName))
    Private Shared ReadOnly NotepadAppId As String = Path.Combine(solutionPath + "\VBWinform\bin\Debug", "VBWinform.exe")

    Private connectionString As String = "Server=127.0.0.1,1432;Database=master;User Id=sa;Password=user@12345;"


    Public Sub New()

        ' データベース初期化
        InitializeDatabase()

        ' CSVファイルからデータを読み込んで、Insert
        Dim csvPath As String = Path.Combine(solutionPath + "\WhiteGUITest", "testdata.csv")
        LoadDataFromCsv(csvPath)

        ' アプリケーション起動
        application = Application.Launch(NotepadAppId)
        Thread.Sleep(500) ' ウィンドウが開くまで待機
        window = application.GetWindow("Form1", InitializeOption.NoCache)
    End Sub
    Public Sub Dispose() Implements IDisposable.Dispose
        ' テスト完了後のクリーンアップ
        CleanupDatabase()
        Try
            If window IsNot Nothing AndAlso Not window.IsClosed Then
                window.Close()
            End If
        Catch ex As Exception
            ' ウィンドウが既に閉じている場合のエラーを無視
        End Try

        If application IsNot Nothing Then
            application.Close()
            application.Dispose()
        End If

    End Sub

    ''' <summary>
    ''' データベースを初期化(テーブルの削除と再作成)
    ''' </summary>
    Private Sub InitializeDatabase()
        Using conn As New SqlConnection(connectionString)
            conn.Open()

            ' 既存テーブルを削除
            Dim dropTableSql As String = "DELETE FROM dbo.TestUsers;"

            Using cmd As New SqlCommand(dropTableSql, conn)
                cmd.ExecuteNonQuery()
            End Using

            Console.WriteLine("データベースを初期化しました。")
        End Using
    End Sub

    ''' <summary>
    ''' CSVファイルからデータを読み込んでInsert
    ''' </summary>
    ''' <param name="csvFilePath">CSVファイルのパス</param>
    Private Sub LoadDataFromCsv(csvFilePath As String)
        If Not File.Exists(csvFilePath) Then
            Console.WriteLine($"CSVファイルが見つかりません: {csvFilePath}")
            Return
        End If

        Using conn As New SqlConnection(connectionString)
            conn.Open()

            ' CSVファイルを読み込み
            Dim lines As String() = File.ReadAllLines(csvFilePath)

            ' ヘッダー行をスキップ(最初の行がヘッダーの場合)
            For i As Integer = 1 To lines.Length - 1
                Dim values As String() = lines(i).Split(","c)

                If values.Length >= 3 Then
                    Dim insertSql As String = "
                            INSERT INTO dbo.TestUsers (Name, Email, Age)
                            VALUES (@Name, @Email, @Age);
                        "

                    Using cmd As New SqlCommand(insertSql, conn)
                        cmd.Parameters.AddWithValue("@Name", values(0).Trim())
                        cmd.Parameters.AddWithValue("@Email", values(1).Trim())

                        Dim age As Integer
                        If Integer.TryParse(values(2).Trim(), age) Then
                            cmd.Parameters.AddWithValue("@Age", age)
                        Else
                            cmd.Parameters.AddWithValue("@Age", 0)
                        End If

                        cmd.ExecuteNonQuery()
                    End Using
                End If
            Next

            Console.WriteLine($"CSVファイルからデータを読み込みました: {lines.Length - 1}件")
        End Using
    End Sub


    ''' <summary>
    ''' データが正しく挿入されたかを確認するヘルパーメソッド
    ''' </summary>
    Private Function GetRecordCount() As Integer
        Using conn As New SqlConnection(connectionString)
            conn.Open()
            Dim countSql As String = "SELECT COUNT(*) FROM dbo.TestUsers;"

            Using cmd As New SqlCommand(countSql, conn)
                Return CInt(cmd.ExecuteScalar())
            End Using
        End Using
    End Function

    ''' <summary>
    ''' データベースをクリーンアップ
    ''' </summary>
    Private Sub CleanupDatabase()
        Using conn As New SqlConnection(connectionString)
            conn.Open()

            ' テストデータを削除
            Dim deleteSql As String = "DELETE FROM dbo.TestUsers;"

            Using cmd As New SqlCommand(deleteSql, conn)
                cmd.ExecuteNonQuery()
            End Using

            Console.WriteLine("データベースをクリーンアップしました。")
        End Using
    End Sub


    <Fact>
    Public Sub レコードの件数が取得できていること()
        Dim textBox = window.Get(Of TextBox)("CountText")
        Assert.Equal("3", textBox.Text)
    End Sub


End Class


参考

TestStack White github

github commit分

2
0
1

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
2
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?