在本文中,将探讨如何使用Microsoft.VisualStudio.TestTools.UnitTesting在VB.NET中实现测试驱动开发(TDD)。测试驱动开发是一种软件开发方法,它要求开发者首先编写测试用例,然后编写能够通过这些测试用例的代码。这种方法有助于确保代码的质量和可维护性。将通过构建一个CUSIP验证器的例子来演示这个过程。
首先,需要创建一个空的类框架,这个类将实现CUSIP验证的逻辑。以下是VB.NET的代码示例:
Public MustInherit Class SecurityIdentifier
Private _securityIdentifier As String
Public ReadOnly Property Identifier As String
Get
Return _securityIdentifier
End Get
End Property
Public Sub New(ByVal securityIdentifier As String)
_securityIdentifier = securityIdentifier
End Sub
End Class
Public Class CusipIdentifier
Inherits SecurityIdentifier
Implements ISecurityIdentifierValidation
Public Function IsValid() As Boolean Implements ISecurityIdentifierValidation.IsValid
Return False
End Function
Public Sub New(ByVal cusipSecurityIdentifier As String)
MyBase.New(cusipSecurityIdentifier)
End Sub
End Class
在这个阶段,验证逻辑尚未实现,因此始终返回False。
接下来,需要创建一个测试套件,用于测试CUSIP验证器。首先,创建一个新的类,并使用TestClass属性进行装饰,以告知测试运行器该类包含测试用例。
Imports System.Text
Imports Microsoft.VisualStudio.TestTools.UnitTesting
<TestClass()>
Public Class CusipUnitTest
End Class
然后,添加一些测试用例来检查类构造函数是否能够正确处理不同的cusipSecurityIdentifier值:
<TestMethod()>
<TestCategory("CUSIP")>
Public Sub CusipEmptyCreateTestMethod()
Dim cusipTest As New CusipIdentifier(String.Empty)
Assert.IsNotNull(cusipTest)
End Sub
<TestMethod()>
<TestCategory("CUSIP")>
Public Sub CusipBlankCreateTestMethod()
Dim cusipTest As New CusipIdentifier("")
Assert.IsNotNull(cusipTest)
End Sub
<TestMethod()>
<TestCategory("CUSIP")>
Public Sub CusipNothingCreateTestMethod()
Dim cusipTest As New CusipIdentifier(Nothing)
Assert.IsNotNull(cusipTest)
End Sub
每个测试用例都以一个Assert语句结束。如果该语句的结果为True,则测试通过;如果结果为False或发生任何异常,则测试失败。
接下来,添加一些正向测试,这些测试用于检查代码在某些预期有效的值上是否能够正常工作。例如,可以选取一组已知有效的公司CUSIP标识符进行测试:
<TestMethod()>
<TestCategory("CUSIP")>
Public Sub CusipAgilentValidTestMethod()
Dim cusipTest As New CusipIdentifier("00846U101")
Dim expected As Boolean = True
Dim actual As Boolean = False
actual = cusipTest.IsValid()
Assert.AreEqual(expected, actual)
End Sub
注意,总是将"expected"变量初始化为与"actual"变量不同的值。这样做是为了确保只有当测试改变了变量值时,测试才能通过。为了获得良好的测试覆盖率,应该将大约30个正确的值编码到测试中。
现在,添加一些负向测试,这些测试用于检查代码是否能够排除预期无效的情况。为了创建负向测试,可以取一个有效的CUSIP,并更改其任何一个构成数字。同样,确保实际变量在测试中发生变化:
<TestMethod()>
<TestCategory("CUSIP")>
Public Sub CusipSprintInValidTestMethod_9()
Dim cusipTest As New CusipIdentifier("852061109")
Dim expected As Boolean = False
Dim actual As Boolean = True
actual = cusipTest.IsValid()
Assert.AreEqual(expected, actual)
End Sub
接下来,即使还没有编写实现验证逻辑的代码,也需要运行测试。这是测试驱动开发的关键区别:首先编写测试用例,然后运行它们以检查它们是否失败。在上面的例子中,只有正向测试会失败,因为验证始终返回False。如果在编写任何业务逻辑代码之前所有测试都通过,那么需要检查测试代码并添加更多的测试。
最后,编写业务逻辑代码,一旦编写完成,就立即运行所有测试。以下是最终工作的CUSIP验证器的代码:
Public Class CusipIdentifier
Inherits SecurityIdentifier
Implements ISecurityIdentifierValidation
Public Function IsValid() As Boolean Implements ISecurityIdentifierValidation.IsValid
If String.IsNullOrWhiteSpace(MyBase.Identifier) Then
Return False
End If
If MyBase.Identifier.Length = 9 Then
Dim checkDigitExpected As Integer = _
GetStringValue(MyBase.Identifier.Substring(0, 8))
Dim checkDigitActual As Integer
If Integer.TryParse(MyBase.Identifier.Substring(8), checkDigitActual) Then
Return (checkDigitActual = checkDigitExpected)
Else
Return False
End If
Else
Return False
End If
End Function
Public Sub New(ByVal cusipSecurityIdentifier As String)
MyBase.New(cusipSecurityIdentifier)
End Sub
Private Function GetLetterValue(ByVal letter As Char) As Integer
Select Case letter
Case "0": Return 0
Case "1": Return 1
Case "2": Return 2
...
Case "Z": Return 35
Case "*": Return 36
Case "@": Return 37
Case "#": Return 38
Case Else: Throw New ArgumentOutOfRangeException("letter", "Specified letter is not part of the CUSIP allowed letter list")
End Select
End Function
Private Function GetStringValue(ByVal cusipString As String) As Integer
If cusipString.Length < 8 Then
Throw New ArgumentOutOfRangeException("cusipString", "CUSIP is too short")
Else
Dim sum As Integer = 0
For letterPos As Integer = 0 To 7 Step 1
Dim v As Integer
If ((letterPos Mod 2) = 1) Then
v = 2 * GetLetterValue(cusipString.Chars(letterPos))
Else
v = GetLetterValue(cusipString.Chars(letterPos))
End If
sum = sum + ((v \ 10) + (v Mod 10))
Next
Return (10 - (sum Mod 10)) Mod 10
End If
End Function
End Class