Specflow 测试用例死锁
Specflow test case deadlocks
我在specflow中写了一个测试用例如下:
[Given(@"I have removed entity")]
public async Task GivenIHaveRemovedEntity(Table table)
{
var employee = table.CreateInstance<TableGeneralInfoDto>();
var schemas = await DataCatalogHooks.SchemasController.Get();
var schema = schemas.Value.First(x => x.Name != null &&
x.Name.Equals("BATCH_TEST", StringComparison.CurrentCultureIgnoreCase));
var objectTypes = await DataCatalogHooks.ObjectTypesController.Get();
var objectType = objectTypes.Value.First(x =>
x.Name.Equals("Table", StringComparison.CurrentCultureIgnoreCase)
);
employee.SchemaId = schema.Id;
employee.SystemName = string.Concat(schema.Name, ".", employee.ObjectName);
employee.ObjectTypeId = objectType.Id;
_scenarioContext["employee"] = employee;
try
{
var entity = await DataCatalogHooks.TablesController.Get(employee.SystemName);
await DataCatalogHooks.TablesController.Delete(entity.Value.Id);
}
catch { }
}
TablesController.Delete() 方法作为异步方法调用:
public async Task DeleteAsync(string id)
{
try
{
var relations = new List<DomainModels.Relation>();
relations.AddRange(_relationRepository.GetBySourceTableIdAsync(id).GetAwaiter().GetResult());
relations.AddRange(_relationRepository.GetByReferenceTableIdAsync(id).GetAwaiter().GetResult());
await _relationRepository.Delete(relations);
await _client.DeleteAsync(id);
}
catch (Exception e)
{
Validation.ExceptionManager.ThrowException(new Exception(e.Message));
throw;
}
}
并且此方法内部调用 http 请求如下:
public async System.Threading.Tasks.Task<SwaggerResponse<EntityCollection>> GetEntityCollectionAsync(string idOrName, string tenantId, System.Threading.CancellationToken cancellationToken)
{
var urlBuilder_ = new System.Text.StringBuilder();
urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/v1.0/{tenantId}/collections/{idOrName}");
urlBuilder_.Replace("{idOrName}", System.Uri.EscapeDataString(ConvertToString(idOrName, System.Globalization.CultureInfo.InvariantCulture)));
urlBuilder_.Replace("{tenantId}", System.Uri.EscapeDataString(ConvertToString(tenantId, System.Globalization.CultureInfo.InvariantCulture)));
var client_ = _httpClient;
try
{
using (var request_ = new System.Net.Http.HttpRequestMessage())
{
request_.Method = new System.Net.Http.HttpMethod("GET");
request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json"));
var url_ = urlBuilder_.ToString();
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
try
{
var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
if (response_.Content != null && response_.Content.Headers != null)
{
foreach (var item_ in response_.Content.Headers)
headers_[item_.Key] = item_.Value;
}
var status_ = ((int)response_.StatusCode).ToString();
if (status_ == "200")
{
var objectResponse_ = await ReadObjectResponseAsync<EntityCollection>(response_, headers_).ConfigureAwait(false);
return new SwaggerResponse<EntityCollection>((int)response_.StatusCode, headers_, objectResponse_.Object);
}
return new SwaggerResponse<EntityCollection>((int)response_.StatusCode, headers_, default(EntityCollection));
}
finally
{
if (response_ != null)
response_.Dispose();
}
}
}
finally
{
}
}
此方法returns结果成功(200)。但在执行此方法后,单元测试继续旋转。没有错误或成功结果显示。
如果我从招摇中调用 Delete API,它工作正常。
.GetAwaiter().GetResult()的用法;在某些情况下,可能会使管理同步上下文的线程死锁,并通过上下文本身死锁。
我删除了 GetAwaiter().GetResult() 并使方法异步。这解决了问题
我在specflow中写了一个测试用例如下:
[Given(@"I have removed entity")]
public async Task GivenIHaveRemovedEntity(Table table)
{
var employee = table.CreateInstance<TableGeneralInfoDto>();
var schemas = await DataCatalogHooks.SchemasController.Get();
var schema = schemas.Value.First(x => x.Name != null &&
x.Name.Equals("BATCH_TEST", StringComparison.CurrentCultureIgnoreCase));
var objectTypes = await DataCatalogHooks.ObjectTypesController.Get();
var objectType = objectTypes.Value.First(x =>
x.Name.Equals("Table", StringComparison.CurrentCultureIgnoreCase)
);
employee.SchemaId = schema.Id;
employee.SystemName = string.Concat(schema.Name, ".", employee.ObjectName);
employee.ObjectTypeId = objectType.Id;
_scenarioContext["employee"] = employee;
try
{
var entity = await DataCatalogHooks.TablesController.Get(employee.SystemName);
await DataCatalogHooks.TablesController.Delete(entity.Value.Id);
}
catch { }
}
TablesController.Delete() 方法作为异步方法调用:
public async Task DeleteAsync(string id)
{
try
{
var relations = new List<DomainModels.Relation>();
relations.AddRange(_relationRepository.GetBySourceTableIdAsync(id).GetAwaiter().GetResult());
relations.AddRange(_relationRepository.GetByReferenceTableIdAsync(id).GetAwaiter().GetResult());
await _relationRepository.Delete(relations);
await _client.DeleteAsync(id);
}
catch (Exception e)
{
Validation.ExceptionManager.ThrowException(new Exception(e.Message));
throw;
}
}
并且此方法内部调用 http 请求如下:
public async System.Threading.Tasks.Task<SwaggerResponse<EntityCollection>> GetEntityCollectionAsync(string idOrName, string tenantId, System.Threading.CancellationToken cancellationToken)
{
var urlBuilder_ = new System.Text.StringBuilder();
urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/v1.0/{tenantId}/collections/{idOrName}");
urlBuilder_.Replace("{idOrName}", System.Uri.EscapeDataString(ConvertToString(idOrName, System.Globalization.CultureInfo.InvariantCulture)));
urlBuilder_.Replace("{tenantId}", System.Uri.EscapeDataString(ConvertToString(tenantId, System.Globalization.CultureInfo.InvariantCulture)));
var client_ = _httpClient;
try
{
using (var request_ = new System.Net.Http.HttpRequestMessage())
{
request_.Method = new System.Net.Http.HttpMethod("GET");
request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json"));
var url_ = urlBuilder_.ToString();
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
try
{
var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
if (response_.Content != null && response_.Content.Headers != null)
{
foreach (var item_ in response_.Content.Headers)
headers_[item_.Key] = item_.Value;
}
var status_ = ((int)response_.StatusCode).ToString();
if (status_ == "200")
{
var objectResponse_ = await ReadObjectResponseAsync<EntityCollection>(response_, headers_).ConfigureAwait(false);
return new SwaggerResponse<EntityCollection>((int)response_.StatusCode, headers_, objectResponse_.Object);
}
return new SwaggerResponse<EntityCollection>((int)response_.StatusCode, headers_, default(EntityCollection));
}
finally
{
if (response_ != null)
response_.Dispose();
}
}
}
finally
{
}
}
此方法returns结果成功(200)。但在执行此方法后,单元测试继续旋转。没有错误或成功结果显示。 如果我从招摇中调用 Delete API,它工作正常。
.GetAwaiter().GetResult()的用法;在某些情况下,可能会使管理同步上下文的线程死锁,并通过上下文本身死锁。
我删除了 GetAwaiter().GetResult() 并使方法异步。这解决了问题