最初に、Visual StudioでASP.NET Core MVC Webアプリケーションを作成します。AspNetCore.TestHost - 並列統合テストの例外を発生させるヘッダミドルウェア
私は、このミドルウェアを追加します。
public async Task Invoke(HttpContext context)
{
context.Response.OnStarting(state =>
{
var resp = ((HttpContext)state).Response;
resp.Headers.Add("MyHeader", "header");
return Task.CompletedTask;
}, context);
await _next.Invoke(context);
}
これは、ブラウザでは正常に動作し、すべてのページ応答で期待どおりヘッダが見られています。すばらしいです。
今、私はnugetパッケージのMicrosoft.AspNetCore.TestHost '、 'のxUnit'、 'xunit.runners' を追加し、テストプロジェクトを作成し、テストに入れ:
[Fact]
public void TestHomePageParallel()
{
using (var server = CreateServer())
{
Parallel.For((long) 0, 10, index =>
{
Get(server, "/");
});
}
}
private TestServer CreateServer()
{
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development");
var directory = Directory.GetCurrentDirectory();
var setDir = Path.GetFullPath(
Path.Combine(directory, @"..\..\..\..\..\WebApplication4")
);
var builder = new WebHostBuilder()
.UseContentRoot(setDir)
.UseStartup<Startup>();
return new TestServer(builder);
}
public void Get(TestServer server, string url)
{
using (var client = server.CreateClient())
{
client.BaseAddress = new Uri("http://localhost:5000");
var req = new HttpRequestMessage();
req.Method = HttpMethod.Get;
req.RequestUri = new Uri(url, UriKind.Relative);
var resp = client.SendAsync(req).Result;
resp.Dispose();
}
}
これはから例外がスローされますミドルウェアのResp.Headers.Add()コード:
One or more errors occurred.
One or more errors occurred.
An item with the same key has already been added.
One or more errors occurred.
An item with the same key has already been added.
One or more errors occurred.
An item with the same key has already been added.
One or more errors occurred.
etc.
これはどうしてですか?
テストループを非パラレルループに変更する場合、例外はありません。
_next.Invoke()呼び出し後にミドルウェアコードを変更すると、問題が消えて例外が記録されないことにも注意してください。しかし、ヘッダを追加し、私はミドルウェア・コードを見てきたすべての例では、は、この方法の周りにそれをしない、と私はコールバックをこのように追加して、おそらく危険なおよび/または遅すぎるということだと思う:
public async Task Invoke(HttpContext context)
{
await _next.Invoke(context);
context.Response.OnStarting(state =>
{
var resp = ((HttpContext)state).Response;
resp.Headers.Add("MyHeader", "header");
return Task.CompletedTask;
}, context);
}
あなたの場合VS2015と.NETコアツールインストール、あなたが解決策を自分でダウンロードし、ここでそれを試すことができていますhttps://dl.dropboxusercontent.com/u/57858722/HeadersNoWorky.zip
Full stack trace:
Test Name: Test.Integration.TestHomePageParallel
Test FullName: Test.Integration.TestHomePageParallel
Test Source: C:\HeadersNoWorky\Test\Test.cs : line 33
Test Outcome: Failed
Test Duration: 0:00:04.226
Result StackTrace:
at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
at System.Threading.Tasks.Parallel.ForWorker64[TLocal](Int64 fromInclusive, Int64 toExclusive, ParallelOptions parallelOptions, Action`1 body, Action`2 bodyWithState, Func`4 bodyWithLocal, Func`1 localInit, Action`1 localFinally)
at System.Threading.Tasks.Parallel.For(Int64 fromInclusive, Int64 toExclusive, Action`1 body)
at Test.Integration.TestHomePageParallel() in C:\HeadersNoWorky\Test\Test.cs:line 36
at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
at Test.Integration.Get(TestServer server, String url) in C:\HeadersNoWorky\Test\Test.cs:line 68
at Test.Integration.<>c__DisplayClass1_0.<TestHomePageParallel>b__0(Int64 index) in C:\HeadersNoWorky\Test\Test.cs:line 38
at System.Threading.Tasks.Parallel.<>c__DisplayClass18_0`1.<ForWorker64>b__1()
at System.Threading.Tasks.Task.InnerInvokeWithArg(Task childTask)
at System.Threading.Tasks.Task.<>c__DisplayClass176_0.<ExecuteSelfReplicating>b__0(Object)
at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
at Microsoft.AspNetCore.Http.HeaderDictionary.Add(String key, StringValues value)
at WebApplication4.MiddlewareHeader.<>c.<Invoke>b__2_0(Object state) in C:\HeadersNoWorky\WebApplication4\MiddlewareHeader.cs:line 20
at Microsoft.AspNetCore.TestHost.ResponseFeature.<>c__DisplayClass23_0.<OnStarting>b__0()
at Microsoft.AspNetCore.TestHost.ResponseFeature.FireOnSendingHeaders()
at Microsoft.AspNetCore.TestHost.ClientHandler.RequestState.GenerateResponse()
at Microsoft.AspNetCore.TestHost.ClientHandler.RequestState.ReturnResponseMessage()
at Microsoft.AspNetCore.TestHost.ClientHandler.RequestState.CompleteResponse()
at Microsoft.AspNetCore.TestHost.ClientHandler.<>c__DisplayClass3_0.<<SendAsync>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.TestHost.ClientHandler.<SendAsync>d__3.MoveNext()
at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
at Test.Integration.Get(TestServer server, String url) in C:\HeadersNoWorky\Test\Test.cs:line 68
at Test.Integration.<>c__DisplayClass1_0.<TestHomePageParallel>b__0(Int64 index) in C:\HeadersNoWorky\Test\Test.cs:line 38
at System.Threading.Tasks.Parallel.<>c__DisplayClass18_0`1.<ForWorker64>b__1()
at System.Threading.Tasks.Task.InnerInvokeWithArg(Task childTask)
at System.Threading.Tasks.Task.<>c__DisplayClass176_0.<ExecuteSelfReplicating>b__0(Object)
at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
at Microsoft.AspNetCore.Http.HeaderDictionary.Add(String key, StringValues value)
at WebApplication4.MiddlewareHeader.<>c.<Invoke>b__2_0(Object state) in C:\HeadersNoWorky\WebApplication4\MiddlewareHeader.cs:line 20
at Microsoft.AspNetCore.TestHost.ResponseFeature.<>c__DisplayClass23_0.<OnStarting>b__0()
at Microsoft.AspNetCore.TestHost.ResponseFeature.FireOnSendingHeaders()
at Microsoft.AspNetCore.TestHost.ClientHandler.RequestState.GenerateResponse()
at Microsoft.AspNetCore.TestHost.ClientHandler.RequestState.ReturnResponseMessage()
at Microsoft.AspNetCore.TestHost.ClientHandler.RequestState.CompleteResponse()
at Microsoft.AspNetCore.TestHost.ClientHandler.<>c__DisplayClass3_0.<<SendAsync>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.TestHost.ClientHandler.<SendAsync>d__3.MoveNext()
at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
at Test.Integration.Get(TestServer server, String url) in C:\HeadersNoWorky\Test\Test.cs:line 68
at Test.Integration.<>c__DisplayClass1_0.<TestHomePageParallel>b__0(Int64 index) in C:\HeadersNoWorky\Test\Test.cs:line 38
at System.Threading.Tasks.Parallel.<>c__DisplayClass18_0`1.<ForWorker64>b__1()
at System.Threading.Tasks.Task.InnerInvokeWithArg(Task childTask)
at System.Threading.Tasks.Task.<>c__DisplayClass176_0.<ExecuteSelfReplicating>b__0(Object)
at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
at Microsoft.AspNetCore.Http.HeaderDictionary.Add(String key, StringValues value)
at WebApplication4.MiddlewareHeader.<>c.<Invoke>b__2_0(Object state) in C:\HeadersNoWorky\WebApplication4\MiddlewareHeader.cs:line 20
at Microsoft.AspNetCore.TestHost.ResponseFeature.<>c__DisplayClass23_0.<OnStarting>b__0()
at Microsoft.AspNetCore.TestHost.ResponseFeature.FireOnSendingHeaders()
at Microsoft.AspNetCore.TestHost.ClientHandler.RequestState.GenerateResponse()
at Microsoft.AspNetCore.TestHost.ClientHandler.RequestState.ReturnResponseMessage()
at Microsoft.AspNetCore.TestHost.ClientHandler.RequestState.CompleteResponse()
at Microsoft.AspNetCore.TestHost.ClientHandler.<>c__DisplayClass3_0.<<SendAsync>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.TestHost.ClientHandler.<SendAsync>d__3.MoveNext()
at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
at Test.Integration.Get(TestServer server, String url) in C:\HeadersNoWorky\Test\Test.cs:line 68
at Test.Integration.<>c__DisplayClass1_0.<TestHomePageParallel>b__0(Int64 index) in C:\HeadersNoWorky\Test\Test.cs:line 38
at System.Threading.Tasks.Parallel.<>c__DisplayClass18_0`1.<ForWorker64>b__1()
at System.Threading.Tasks.Task.InnerInvokeWithArg(Task childTask)
at System.Threading.Tasks.Task.<>c__DisplayClass176_0.<ExecuteSelfReplicating>b__0(Object)
at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
at Microsoft.AspNetCore.Http.HeaderDictionary.Add(String key, StringValues value)
at WebApplication4.MiddlewareHeader.<>c.<Invoke>b__2_0(Object state) in C:\HeadersNoWorky\WebApplication4\MiddlewareHeader.cs:line 20
at Microsoft.AspNetCore.TestHost.ResponseFeature.<>c__DisplayClass23_0.<OnStarting>b__0()
at Microsoft.AspNetCore.TestHost.ResponseFeature.FireOnSendingHeaders()
at Microsoft.AspNetCore.TestHost.ClientHandler.RequestState.GenerateResponse()
at Microsoft.AspNetCore.TestHost.ClientHandler.RequestState.ReturnResponseMessage()
at Microsoft.AspNetCore.TestHost.ClientHandler.RequestState.CompleteResponse()
at Microsoft.AspNetCore.TestHost.ClientHandler.<>c__DisplayClass3_0.<<SendAsync>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.TestHost.ClientHandler.<SendAsync>d__3.MoveNext()
at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
at Test.Integration.Get(TestServer server, String url) in C:\HeadersNoWorky\Test\Test.cs:line 68
at Test.Integration.<>c__DisplayClass1_0.<TestHomePageParallel>b__0(Int64 index) in C:\HeadersNoWorky\Test\Test.cs:line 38
at System.Threading.Tasks.Parallel.<>c__DisplayClass18_0`1.<ForWorker64>b__1()
at System.Threading.Tasks.Task.InnerInvokeWithArg(Task childTask)
at System.Threading.Tasks.Task.<>c__DisplayClass176_0.<ExecuteSelfReplicating>b__0(Object)
at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
at Microsoft.AspNetCore.Http.HeaderDictionary.Add(String key, StringValues value)
at WebApplication4.MiddlewareHeader.<>c.<Invoke>b__2_0(Object state) in C:\HeadersNoWorky\WebApplication4\MiddlewareHeader.cs:line 20
at Microsoft.AspNetCore.TestHost.ResponseFeature.<>c__DisplayClass23_0.<OnStarting>b__0()
at Microsoft.AspNetCore.TestHost.ResponseFeature.FireOnSendingHeaders()
at Microsoft.AspNetCore.TestHost.ClientHandler.RequestState.GenerateResponse()
at Microsoft.AspNetCore.TestHost.ClientHandler.RequestState.ReturnResponseMessage()
at Microsoft.AspNetCore.TestHost.ClientHandler.RequestState.CompleteResponse()
at Microsoft.AspNetCore.TestHost.ClientHandler.<>c__DisplayClass3_0.<<SendAsync>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.TestHost.ClientHandler.<SendAsync>d__3.MoveNext()
at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
at Test.Integration.Get(TestServer server, String url) in C:\HeadersNoWorky\Test\Test.cs:line 68
at Test.Integration.<>c__DisplayClass1_0.<TestHomePageParallel>b__0(Int64 index) in C:\HeadersNoWorky\Test\Test.cs:line 38
at System.Threading.Tasks.Parallel.<>c__DisplayClass18_0`1.<ForWorker64>b__1()
at System.Threading.Tasks.Task.InnerInvokeWithArg(Task childTask)
at System.Threading.Tasks.Task.<>c__DisplayClass176_0.<ExecuteSelfReplicating>b__0(Object)
at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
at Microsoft.AspNetCore.Http.HeaderDictionary.Add(String key, StringValues value)
at WebApplication4.MiddlewareHeader.<>c.<Invoke>b__2_0(Object state) in C:\HeadersNoWorky\WebApplication4\MiddlewareHeader.cs:line 20
at Microsoft.AspNetCore.TestHost.ResponseFeature.<>c__DisplayClass23_0.<OnStarting>b__0()
at Microsoft.AspNetCore.TestHost.ResponseFeature.FireOnSendingHeaders()
at Microsoft.AspNetCore.TestHost.ClientHandler.RequestState.GenerateResponse()
at Microsoft.AspNetCore.TestHost.ClientHandler.RequestState.ReturnResponseMessage()
at Microsoft.AspNetCore.TestHost.ClientHandler.RequestState.CompleteResponse()
at Microsoft.AspNetCore.TestHost.ClientHandler.<>c__DisplayClass3_0.<<SendAsync>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.TestHost.ClientHandler.<SendAsync>d__3.MoveNext()
at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
at Test.Integration.Get(TestServer server, String url) in C:\HeadersNoWorky\Test\Test.cs:line 68
at Test.Integration.<>c__DisplayClass1_0.<TestHomePageParallel>b__0(Int64 index) in C:\HeadersNoWorky\Test\Test.cs:line 38
at System.Threading.Tasks.Parallel.<>c__DisplayClass18_0`1.<ForWorker64>b__1()
at System.Threading.Tasks.Task.InnerInvokeWithArg(Task childTask)
at System.Threading.Tasks.Task.<>c__DisplayClass176_0.<ExecuteSelfReplicating>b__0(Object)
at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
at Microsoft.AspNetCore.Http.HeaderDictionary.Add(String key, StringValues value)
at WebApplication4.MiddlewareHeader.<>c.<Invoke>b__2_0(Object state) in C:\HeadersNoWorky\WebApplication4\MiddlewareHeader.cs:line 20
at Microsoft.AspNetCore.TestHost.ResponseFeature.<>c__DisplayClass23_0.<OnStarting>b__0()
at Microsoft.AspNetCore.TestHost.ResponseFeature.FireOnSendingHeaders()
at Microsoft.AspNetCore.TestHost.ClientHandler.RequestState.GenerateResponse()
at Microsoft.AspNetCore.TestHost.ClientHandler.RequestState.ReturnResponseMessage()
at Microsoft.AspNetCore.TestHost.ClientHandler.RequestState.CompleteResponse()
at Microsoft.AspNetCore.TestHost.ClientHandler.<>c__DisplayClass3_0.<<SendAsync>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.TestHost.ClientHandler.<SendAsync>d__3.MoveNext()
at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
at Test.Integration.Get(TestServer server, String url) in C:\HeadersNoWorky\Test\Test.cs:line 68
at Test.Integration.<>c__DisplayClass1_0.<TestHomePageParallel>b__0(Int64 index) in C:\HeadersNoWorky\Test\Test.cs:line 38
at System.Threading.Tasks.Parallel.<>c__DisplayClass18_0`1.<ForWorker64>b__1()
at System.Threading.Tasks.Task.InnerInvokeWithArg(Task childTask)
at System.Threading.Tasks.Task.<>c__DisplayClass176_0.<ExecuteSelfReplicating>b__0(Object)
at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
at Microsoft.AspNetCore.Http.HeaderDictionary.Add(String key, StringValues value)
at WebApplication4.MiddlewareHeader.<>c.<Invoke>b__2_0(Object state) in C:\HeadersNoWorky\WebApplication4\MiddlewareHeader.cs:line 20
at Microsoft.AspNetCore.TestHost.ResponseFeature.<>c__DisplayClass23_0.<OnStarting>b__0()
at Microsoft.AspNetCore.TestHost.ResponseFeature.FireOnSendingHeaders()
at Microsoft.AspNetCore.TestHost.ClientHandler.RequestState.GenerateResponse()
at Microsoft.AspNetCore.TestHost.ClientHandler.RequestState.ReturnResponseMessage()
at Microsoft.AspNetCore.TestHost.ClientHandler.RequestState.CompleteResponse()
at Microsoft.AspNetCore.TestHost.ClientHandler.<>c__DisplayClass3_0.<<SendAsync>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.TestHost.ClientHandler.<SendAsync>d__3.MoveNext()
Result Message:
One or more errors occurred.
One or more errors occurred.
An item with the same key has already been added.
One or more errors occurred.
An item with the same key has already been added.
One or more errors occurred.
An item with the same key has already been added.
One or more errors occurred.
An item with the same key has already been added.
One or more errors occurred.
An item with the same key has already been added.
One or more errors occurred.
An item with the same key has already been added.
One or more errors occurred.
An item with the same key has already been added.
One or more errors occurred.
An item with the same key has already been added.
UPDATE:いくつかのデバッグを追加した後、私はResponse.OnStarting()が呼び出されていることを を見ることができますsのために複数回これは、1つの要求に対して応答ヘッダーを2回更新しようとしているため、例外が発生します。残念ながら、なぜResponse.OnStarting()が2回呼び出されるのか分かりません。
が欠落しているいくつかのクロススレッド同期のように見えますか? –
これはとにかくテストするのは本当に奇妙な方法のようですが、どうしてHTTPリクエストを使ってコードをテストしたいのですか? – DavidG
両方のコメントに答えるために、私はそれを簡略化しました。現実の世界では、100以上の独立したテストが存在しますが(テストランナーを介して並行して実行されます)、問題を作成します。私は並列ループを単一のテストに入れて、単一のテストで問題をシミュレートしました。 – jimasp