2016-11-17 81 views
0

我有一个Module,我想用来缓存一些东西。这很简单。我想避开ConcurrentDictionary,因为它需要有保证的操作。SyncLock不能在单元测试中工作

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. 

回答

1

我想通了。这个问题不是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