VB.NET用単体テストフレームワーク(簡易版) [プログラミング]
前回の内容で薄々分かっていたかもしれませんが、Visual Basic .NET用の単体テストフレームワークっぽいものを作ってみました。
.NET 用の単体テストフレームワークは NUnit が有名で、VB.NET でも使えるらしいので、NUnit を使うことをお勧めしておきます。
''' <summary>簡易単体テストフレームワーク</summary> Public Class UnitTest ''' <summary>テスト失敗を通知する例外</summary> Private Class UnitTestAssertException Inherits Exception ''' <summary>失敗したテストの実装場所を表す情報</summary> Private sf_ As StackFrame ''' <summary>コンストラクタ</summary> ''' <param name="sf">失敗したテストの実装場所</param> ''' <param name="mes">何らかのメッセージ。不要なら Nothing</param> Sub New(ByVal sf As StackFrame, ByVal mes As String) MyBase.New(IIf(IsNothing(mes), "", mes)) sf_ = sf End Sub ''' <summary>失敗したテストの実装場所</summary> Public ReadOnly Property PlaceTestCode() As StackFrame Get Return sf_ End Get End Property End Class ''' <summary>テストメソッドに関する情報</summary> Private Class TestMethod ''' <summary>テストメソッドの実行対象となるオブジェクト</summary> Private obj_ As Object ''' <summary>テストメソッド</summary> Private method_ As Reflection.MethodInfo ''' <summary>コンストラクタ</summary> ''' <param name="obj">テストメソッドの実行対象となるオブジェクト</param> ''' <param name="method">テストメソッド</param> Sub New(ByVal obj As Object, ByVal method As Reflection.MethodInfo) obj_ = obj method_ = method End Sub ''' <summary>テストメソッドを実行する</summary> ''' ''' <exception cref="System.Reflection.TargetInvocationException"> ''' テストメソッド中に何らかの例外 ''' </exception> Public Sub invoke() method_.Invoke(obj_, Nothing) End Sub ''' <summary>テストメソッドについての情報</summary> Public ReadOnly Property method() As Reflection.MethodInfo Get Return method_ End Get End Property End Class ''' <summary>全てのテストが完了したことを通知するイベント</summary> ''' <param name="succeeded">成功したテストの件数</param> ''' <param name="total">全てのテストの件数</param> Public Event NotifyFinished(ByVal succeeded As Integer, ByVal total As Integer) ''' <summary>失敗したテストについて通知するイベント</summary> ''' <param name="sf">失敗したテストの実装場所</param> ''' <param name="mes">テスト結果判定時に指定したメッセージ</param> Public Event NotifyFailed(ByVal sf As StackFrame, ByVal mes As String) ''' <summary>何らかの例外が発生したテストについて通知するイベント</summary> ''' <param name="method">テストメソッドの情報</param> ''' <param name="ex">発生した例外</param> Public Event NotifyError(ByVal method As Reflection.MethodInfo, ByVal ex As Exception) ''' <summary>実施するテストメソッドの一覧</summary> ''' <remarks>TestMethod型のオブジェクトを格納している</remarks> Private listTestMethod_ As New ArrayList ''' <summary>テストコードを登録する</summary> ''' ''' <remarks> ''' 指定したオブジェクトのクラスに存在する 「test」で始まるメソッド を、 ''' テストコードとして登録する。 ''' </remarks> ''' ''' <param name="obj">テストメソッドの実行対象となるオブジェクト</param> Public Sub addTestCase(ByVal obj As Object) Dim methods As System.Reflection.MethodInfo() = _ obj.GetType().GetMethods(Reflection.BindingFlags.Instance Or _ Reflection.BindingFlags.Public Or _ Reflection.BindingFlags.NonPublic Or _ Reflection.BindingFlags.DeclaredOnly) Dim method As System.Reflection.MethodInfo For Each method In methods If method.Name.StartsWith("test") Then listTestMethod_.Add(New TestMethod(obj, method)) End If Next End Sub ''' <summary>テストを実施する</summary> ''' ''' <remarks> ''' テスト完了時には、イベント NotifyFinished を発行する。 ''' テストに失敗したら、その都度、イベント NotifyFailed を発行する。 ''' テストで何らかの例外が発生したら、イベント NotifyError を発行する。 ''' </remarks> ''' ''' <seealso cref="UnitTest.NotifyFinished" /> ''' <seealso cref="UnitTest.NotifyFailed" /> ''' <seealso cref="UnitTest.NotifyError" /> Public Sub runTest() Dim total As Integer = 0 Dim succeeded As Integer = 0 Dim info As TestMethod For Each info In listTestMethod_ If runTestMethod(info) Then succeeded = succeeded + 1 End If total = total + 1 Next RaiseEvent NotifyFinished(succeeded, total) End Sub ''' <summary>テストメソッドを実行する。</summary> ''' ''' <remarks> ''' テスト完了時には、イベント NotifyFinished を発行する。 ''' テストに失敗したら、その都度、イベント NotifyFailed を発行する。 ''' テストで何らかの例外が発生したら、イベント NotifyError を発行する。 ''' </remarks> ''' ''' <param name="test">実行するテストメソッド</param> ''' <seealso cref="UnitTest.NotifyFinished" /> ''' <seealso cref="UnitTest.NotifyFailed" /> ''' <seealso cref="UnitTest.NotifyError" /> Private Function runTestMethod(ByVal test As TestMethod) As Boolean runTestMethod = False Try test.invoke() runTestMethod = True Catch ex0 As Reflection.TargetInvocationException ' Invoke で呼んだ先での例外は全て↑の例外でラップ If TypeOf ex0.InnerException Is UnitTestAssertException Then ' テストに失敗 Dim ex As UnitTestAssertException = ex0.InnerException Dim sf As StackFrame = ex.PlaceTestCode Dim mes As String = ex.Message Trace.WriteLine(sf.GetFileName & "(" & sf.GetFileLineNumber & ")" & _ " : assert: " & mes) RaiseEvent NotifyFailed(sf, mes) Else ' テスト失敗以外の何らかの例外 RaiseEvent NotifyError(test.method, ex0.InnerException) End If End Try End Function ''' <summary>テストの判定を行う</summary> ''' ''' <remarks> ''' テストメソッド内で、結果を判定するのに使う。 ''' </remarks> ''' ''' <param name="result">True = テストOK / False = テストNG</param> ''' <param name="mes">テストに失敗した時に使用する何らかのメッセージ</param> Public Shared Sub assert(ByVal result As Boolean, _ Optional ByVal mes As String = Nothing) If Not result Then Throw New UnitTest.UnitTestAssertException(New StackFrame(1, True), mes) End If End Sub ''' <summary>テストの判定を行う</summary> ''' ''' <remarks> ''' テストメソッド内で、結果が期待する値と等しいかどうかを判定するのに使う。 ''' </remarks> ''' ''' <param name="o1">期待する値</param> ''' <param name="o2">結果</param> ''' <param name="mes">テストに失敗した時に使用する何らかのメッセージ</param> Public Shared Sub assertEquals(ByVal o1 As Object, ByVal o2 As Object, _ Optional ByVal mes As String = Nothing) Dim result As Boolean = False If IsNothing(o1) Then If IsNothing(o2) Then result = True End If ElseIf o1.Equals(o2) Then result = True End If If IsNothing(mes) Then mes = String.Empty End If mes = mes & " [ " & o1 & " / " & o2 & " ]" If Not result Then Throw New UnitTestAssertException(New StackFrame(1, True), mes) End If End Sub End Class
test
で始まるメソッドを用意して、テストコードを書きます。
テストの判定は、UnitTest.assert()
または UnitTest.assertEquals()
で行います。
Private Sub testXXX() Dim target As New XXX() ' func()の戻り値が True になるのが正しい UnitTest.assert(target.func()) ' getA()の戻り値が 10 になるのが正しい UnitTest.assertEquals(10, target.getA()) End Sub
テストの開始は、addTestCase
メソッドで、テストコードのあるクラスのインスタンスを追加して、runTest
で実行開始です。
Dim ut As UnitTest ut = New UnitTest ut.addTestCase(New TestClass1) ut.addTestCase(New TestClass2) ut.addTestCase(New TestClass3) ut.runTest()
テストの結果はイベントで通知します。
' テスト完了時 Private Sub TestFinished(ByVal succeeded As Integer, ByVal total As Integer) _ Handles ut.NotifyFinished MsgBox(succeeded & " / " & total, , "テスト結果") End Sub ' テスト失敗時 Private Sub TestFailed(ByVal sf As StackFrame, ByVal mes As String) _ Handles ut.NotifyFailed Dim info As String info = sf.GetMethod().DeclaringType.Name & "." & _ sf.GetMethod().Name & _ " (" & sf.GetFileLineNumber() & ")" If Not IsNothing(mes) AndAlso mes.Length > 0 Then info = info & vbCrLf & vbCrLf & mes End If MsgBox(info, , "テスト失敗") End Sub ' テスト中に例外発生時 Private Sub TestError(ByVal method As Reflection.MethodInfo, ByVal ex As Exception) _ Handles ut.NotifyError Dim mes As String mes = method.DeclaringType.Name & "." & method.Name mes = mes & vbCrLf & vbCrLf & ex.Message MsgBox(mes, , "テストエラー") End Sub
今日の一冊 | |
|
2009-12-22 07:46
nice!(0)
コメント(0)
トラックバック(0)
コメント 0