单元测试中SyncLock不能工作



我有一个Module,我想用来缓存一些东西。这很简单。我想避开ConcurrentDictionary,因为它需要是一个有保证的操作。

Public Module SchemaTableCache
    Private lockObject As New Object
    Private columnCache As New Dictionary(Of String, SortedSet(Of String))
    <Extension>
    Public Sub CacheSchemaTable(dataReader As IDataReader, name As String)
        SyncLock lockObject
            Dim rows As New List(Of DataRow)
            If columnCache.ContainsKey(name) Then
                Return
            End If
            rows = dataReader.GetSchemaTable().Rows.OfType(Of DataRow)().ToList()
            columnCache.Add(name, New SortedSet(Of String)(rows.Select(Function(r) r.Field(Of String)("ColumnName"))))
        End SyncLock
    End Sub
    <Extension>
    Public Function HasColumn(name As String, column As String) As Boolean
        SyncLock lockObject
            Dim cols As New SortedSet(Of String)
            If Not columnCache.TryGetValue(name, cols) Then
                Return False
            End If
            Return cols.Contains(column)
        End SyncLock
    End Function
End Module

是这样的。我有一些单元测试来测试利用HasColumn函数的代码。我这样设置这些测试:

dataReader.Setup(Function(x) x(field)).Returns(val)
' setup the schema table
Dim table As New DataTable()
table.Columns.Add("ColumnName", GetType(String))
If setupTable Then
    table.Rows.Add(field)
End If
dataReader.Setup(Function(x) x.GetSchemaTable()) _
    .Returns(table)
dataReader.Object.CacheSchemaTable("table")

然后测试这个函数:

Dim typeName = GetType(T).Name
Debug.WriteLine($"IDataReader_Value({schemaTableName}.{column})")
If Not schemaTableName.HasColumn(column) Then
    Debug.WriteLine($"Could not find column {column}; returning default value.")
    Return typeName.DefaultValue()
End If
Dim input = dr(column)
Debug.WriteLine($"Found column {column}; returning value {input}.")
Return Value(Of T)(input)

你可以在这里看到我点击了HasColumn方法。事情是这样的。如果我单独执行这些测试,它们就会成功;但是,如果我执行整个测试集,它们就会失败。

显然这里有线程安全问题,但我不能为我的生活弄清楚我做错了什么。谁能告诉我哪里出错了?

测试失败时的输出是:

Test Name:  IDataReader_ValueBoolean
Test Outcome:   Failed
Result Message: Assert.AreEqual failed. Expected:<True>. Actual:<False>.
Result StandardOutput:  
Debug Trace:
IDataReader_Value(table.field)
Could not find column field; returning default value.

测试成功时的输出是:

Test Name:  IDataReader_ValueBoolean
Test Outcome:   Passed
Result StandardOutput:  
Debug Trace:
IDataReader_Value(table.field)
Found column field; returning value True.

我明白了。问题不在于SyncLock,而在于我的逻辑。每个测试都针对不同的问题。有些人正在测试缺失的专栏,而有些人则期待它的存在。因此,我需要能够更新缓存。

下面是新的逻辑:

SyncLock lockObject
    Debug.WriteLine($"Caching schema table {name}.")
    Dim rows As New List(Of DataRow)
    If Not columnCache.ContainsKey(name) Then
        Debug.WriteLine($"Adding cache key for {name}.")
        columnCache.Add(name, New SortedSet(Of String)())
    End If
    rows = dataReader.GetSchemaTable().Rows.OfType(Of DataRow)().ToList()
    Debug.WriteLine($"Schema table rows count: {rows.Count}")
    columnCache(name) = New SortedSet(Of String)(rows.Select(Function(r) r.Field(Of String)("ColumnName")))
    Debug.WriteLine($"Successfully cached {name}.")
End SyncLock

最新更新