You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在VB.NET中模拟迭代式数据库查询并保留原有功能?

解决方案:通过依赖注入模拟迭代式数据检索

要在保留原有功能的前提下实现单元测试友好的重构,核心是抽象数据访问层并通过依赖注入替换硬编码的实例化,具体步骤如下:

1. 抽象DataAccess层为接口

首先为DataAccess类定义对应的接口,覆盖函数中用到的核心方法和关联类型:

' 抽象资源类型枚举
Public Enum ReTypes
    DG
    ' 保留原有其他枚举值
End Enum

' 抽象资源子项接口
Public Interface IRegResourceChild
    Property ID As Integer
    Property SignalID As Integer
End Interface

' 抽象资源接口
Public Interface IRegResource
    Property Type As ReTypes
    Property Children As IEnumerable(Of IRegResourceChild)
End Interface

' 核心数据访问接口
Public Interface IDataAccess
    Sub InitializeFromMDB(connectionString As String, dbName As String)
    Function GetRegResource(reID As Integer, signalID As Integer, effDayUtc As Date) As IRegResource
End Interface

' 让原有DataAccess类实现该接口
Public Class DataAccess
    Implements IDataAccess

    ' 保留原有InitializeFromMDB实现
    Public Sub InitializeFromMDB(connectionString As String, dbName As String) Implements IDataAccess.InitializeFromMDB
        ' 原有代码逻辑
    End Sub

    ' 调整原有GetRegResource实现,返回IRegResource类型
    Public Function GetRegResource(reID As Integer, signalID As Integer, effDayUtc As Date) As IRegResource Implements IDataAccess.GetRegResource
        ' 原有代码逻辑,返回实现了IRegResource的实例
    End Function
End Class

2. 修改函数以支持依赖注入

为原Shared函数新增IDataAccess可选参数,移除内部硬编码的New DataAccess实例化逻辑,同时保留原有调用兼容性:

' 修改后的函数签名,新增IDataAccess可选参数
Public Shared Function CalcDailyScoresByDate(ByVal dayUtc As Date,
                                             ByVal ReID As Integer,
                                             ByVal dailyScores As DataTable,
                                             Optional ByVal regDs As IDataAccess = Nothing
                                             ) As DailyDataCollection
    Dim timeUtc As Date = dayUtc 
    Dim srvSet As ServiceSettings = New ServiceSettings(dayUtc)
    Dim ept As Date = TimeZoneInfo.ConvertTimeFromUtc(timeUtc, TimeZoneInfo.Local)
    ept = ept.AddDays(1)
    Dim effDayUtc As Date = TimeZoneInfo.ConvertTimeToUtc(ept)
    Dim daily As New DailyDataCollection
        
    Try
        #Region "Region DataAccess"
        ' 生产环境自动创建默认实现,保证原有逻辑不变
        If regDs Is Nothing Then
            regDs = New DataAccess()
            Try
                regDs.InitializeFromMDB(srvSet.MyConnectionString, "MyDb")
            Catch ex As Exception
                MyUtil.ErrorLog("RegDS.InitializeFromMDB " & ex.Message)
            End Try
        End If
        
        For Each rw In dailyScores.Rows
            Dim drw As New DailyData

            drw.ReID = rw(0)
            drw.SignalID = rw(1) 
            drw.MyDay = effDayUtc
            drw.dailyScore = rw(2) 
        
            ' 使用注入的IDataAccess实例调用数据检索方法
            Dim rsc = regDs.GetRegResource(drw.ReID, drw.SignalID, effDayUtc)  
        
            If rsc.Type = ReTypes.DG Then                              
                For Each child In rsc.Children                                                
                    Dim childDaily As New DailyData
                    childDaily.ReID = child.ID
                    childDaily.SignalID = child.SignalID
                    childDaily.myDay = effDayUtc
                    childDaily.dailyScore = drw.dailyScore
                    daily.Add(childDaily)                                                      
                Next
            End If
                            
        Next
        dailyScores.Dispose()
        dailyScores = Nothing
        srvSet = Nothing
        
        #End Region
            
    Catch ex As Exception
        MyUtil.ErrorLog("CalcDailyScoresByDate: " & ex.Message)
    End Try
            
    Return daily
End Function

3. 单元测试中模拟迭代数据检索

使用Mock框架(如Moq)创建IDataAccess的模拟实例,预设GetRegResource方法根据不同参数返回模拟资源,从而测试迭代逻辑:

Imports Moq
Imports Microsoft.VisualStudio.TestTools.UnitTesting

<TestClass>
Public Class DailyScoresTests
    <TestMethod>
    Public Sub CalcDailyScoresByDate_WithDGTypeResource_AddsChildRecords()
        ' 1. 准备测试用DataTable
        Dim testDayUtc = Date.UtcNow
        Dim testReID = 1
        Dim testDataTable = New DataTable()
        testDataTable.Columns.AddRange({
            New DataColumn("ReID", GetType(Integer)),
            New DataColumn("SignalID", GetType(Integer)),
            New DataColumn("DailyScore", GetType(Decimal))
        })
        testDataTable.Rows.Add(100, 200, 85.5)

        ' 2. 模拟资源子项
        Dim mockChild = New Mock(Of IRegResourceChild)()
        mockChild.Setup(Function(c) c.ID).Returns(300)
        mockChild.Setup(Function(c) c.SignalID).Returns(400)

        ' 3. 模拟DG类型资源
        Dim mockResource = New Mock(Of IRegResource)()
        mockResource.Setup(Function(r) r.Type).Returns(ReTypes.DG)
        mockResource.Setup(Function(r) r.Children).Returns(New List(Of IRegResourceChild) From {mockChild.Object})

        ' 4. 模拟数据访问层,预设检索方法返回值
        Dim mockDataAccess = New Mock(Of IDataAccess)()
        mockDataAccess.Setup(Function(d) d.GetRegResource(100, 200, It.IsAny(Of Date))) _
                      .Returns(mockResource.Object)

        ' 5. 调用目标函数
        Dim result = CalcDailyScoresByDate(testDayUtc, testReID, testDataTable, mockDataAccess.Object)

        ' 6. 验证结果
        Assert.AreEqual(1, result.Count)
        Assert.AreEqual(300, result(0).ReID)
        Assert.AreEqual(400, result(0).SignalID)
    End Sub
End Class

关键说明

  • 完全保留原有生产逻辑:生产环境调用时可省略regDs参数,函数自动创建并初始化真实DataAccess实例。
  • 覆盖所有分支测试:通过Mock可以轻松模拟不同参数下的返回结果,测试非DG类型、空Children集合等场景。
  • 最小侵入式修改:仅新增接口和参数,无需大幅改动原有业务逻辑。

内容的提问来源于stack exchange,提问作者user2284452

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 03:20:25