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.

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
Другие вопросы по тегам