応答メッセージへのリンクデータの追加を処理するために、サービスにアクションフィルターを追加しようとしていました。HttpActionExecutedContextをモックする必要があることがわかりましたが、モックするのは難しいクラスです。アクションフィルターのテストをどのように処理していますか?
6 に答える
HttpActionExecutedContext
以下のように偽物を作成できます。
public static HttpActionContext CreateActionContext(HttpControllerContext controllerContext = null, HttpActionDescriptor actionDescriptor = null)
{
HttpControllerContext context = controllerContext ?? ContextUtil.CreateControllerContext();
HttpActionDescriptor descriptor = actionDescriptor ?? new Mock<HttpActionDescriptor>() { CallBase = true }.Object;
return new HttpActionContext(context, descriptor);
}
public static HttpActionExecutedContext GetActionExecutedContext(HttpRequestMessage request, HttpResponseMessage response)
{
HttpActionContext actionContext = CreateActionContext();
actionContext.ControllerContext.Request = request;
HttpActionExecutedContext actionExecutedContext = new HttpActionExecutedContext(actionContext, null) { Response = response };
return actionExecutedContext;
}
ASP.NET WebAPIソースコードであるContextUtilクラスからそのコードをコピーして貼り付けました。いくつかの組み込みフィルターをテストした方法の例をいくつか示します。
ActionFilterAttributeTest
ActionFilterAttribute
は抽象クラスであるテストクラスですが、あなたはアイデアを得るでしょう。
ちょうど新しいもの。
private HttpActionContext CreateExecutingContext()
{
return new HttpActionContext { ControllerContext = new HttpControllerContext { Request = new HttpRequestMessage() } };
}
private HttpActionExecutedContext CreateExecutedContextWithStatusCode(HttpStatusCode statusCode)
{
return new HttpActionExecutedContext
{
ActionContext = new HttpActionContext
{
ControllerContext = new HttpControllerContext
{
Request = new HttpRequestMessage()
}
},
Response = new HttpResponseMessage
{
StatusCode = statusCode,
Content = new StringContent("blah")
}
};
}
作成したカスタムの未処理の例外フィルターをテストしようとしたときにも同じ問題が発生しました。
これでうまくいきました。たくさんの更新と非常に長いコード行。
var httpActionExecutedContext = new HttpActionExecutedContext(
new HttpActionContext(
new HttpControllerContext(
new HttpConfiguration(),
Substitute.For<IHttpRouteData>(),
new HttpRequestMessage()),
Substitute.For<HttpActionDescriptor>()),
null);
NSubstiuteが使用されましたが、抽象基本クラスを処理する任意のモックフレームワークで問題ありません。
お役に立てれば
これもレンガの壁に頭をぶつけてきました。contextUtilを試しましたが、null参照例外が発生し続けました。この投稿でactionFilterを呼び出す方法を見つけました。NB フィルターのモックインスタンスを使用しているときにactionFilterが呼び出されていなかったため、実際のオブジェクトを使用する必要がありました。HTH
具体的には:
var httpActionContext = new HttpActionContext
{
ControllerContext = new HttpControllerContext
{
Request = requestMessage
}
};
//call filter
var filter = new FooFilter();
filter.OnActionExecuting(httpActionContext);
https://stackoverflow.com/a/44447349/5547177を参照
次の方法でHTTPActionContextを自分で作成できます。
_ctx = new HttpActionContext
{
ControllerContext = new HttpControllerContext()
{
Request = new HttpRequestMessage()
}
};
_ctx.Request.Properties[System.Web.Http.Hosting.HttpPropertyKeys.HttpConfigurationKey] = new HttpConfiguration();
トリックは、Request.Propertiesエントリ設定がない場合で、次のエラーが表示されます。
要求に関連付けられた構成オブジェクトがないか、提供された構成がnullでした。
HTTPActionContextコンストラクターでHTTPConfigurationを設定できるため、これは設計者の見落としかもしれません。
これが2018(.NET Framework 4.5.1)の実例です。ExceptionFilterAttributeを使用しますが、他のFilterAttributeと同様である必要があります。
[Test]
public void MyTest()
{
var request = new HttpRequestMessage(HttpMethod.Get, new Uri("http://www.google.com"));
var response = new HttpResponseMessage();
// This next line is necessary to avoid the following error
// if you call `context.Request.CreateResponse(...)` inside the filter:
// System.InvalidOperationException: The request does not have an associated configuration object or the provided configuration was null.
// Discovered from https://stackoverflow.com/a/44447355/3312114
request.SetConfiguration(new HttpConfiguration());
var context = ContextUtil.GetActionExecutedContext(request, response);
_myFilter.OnException(context); // Execute your methods
Assert.AreEqual(HttpStatusCode.InternalServerError, context.Response.StatusCode); // Make your assertions
}
次に、ContextUtilクラスをテストプロジェクトのどこかにコピーします。@tugberkの回答に対する@thomasbのコメントは、最新のコードがCodeplexにあることを示唆しています。そのコメントは2014年のものだったので、後のコードもあるかもしれませんが、元のリンクされたコードは機能しませんでしたが、2014年のコードは(2018年1月に)機能しました。便宜上、以下のバージョンをコピーしました。これを新しいファイルにドロップするだけです。
internal static class ContextUtil
{
public static HttpControllerContext CreateControllerContext(HttpConfiguration configuration = null, IHttpController instance = null, IHttpRouteData routeData = null, HttpRequestMessage request = null)
{
HttpConfiguration config = configuration ?? new HttpConfiguration();
IHttpRouteData route = routeData ?? new HttpRouteData(new HttpRoute());
HttpRequestMessage req = request ?? new HttpRequestMessage();
req.SetConfiguration(config);
req.SetRouteData(route);
HttpControllerContext context = new HttpControllerContext(config, route, req);
if (instance != null)
{
context.Controller = instance;
}
context.ControllerDescriptor = CreateControllerDescriptor(config);
return context;
}
public static HttpActionContext CreateActionContext(HttpControllerContext controllerContext = null, HttpActionDescriptor actionDescriptor = null)
{
HttpControllerContext context = controllerContext ?? ContextUtil.CreateControllerContext();
HttpActionDescriptor descriptor = actionDescriptor ?? CreateActionDescriptor();
descriptor.ControllerDescriptor = context.ControllerDescriptor;
return new HttpActionContext(context, descriptor);
}
public static HttpActionContext GetHttpActionContext(HttpRequestMessage request)
{
HttpActionContext actionContext = CreateActionContext();
actionContext.ControllerContext.Request = request;
return actionContext;
}
public static HttpActionExecutedContext GetActionExecutedContext(HttpRequestMessage request, HttpResponseMessage response)
{
HttpActionContext actionContext = CreateActionContext();
actionContext.ControllerContext.Request = request;
HttpActionExecutedContext actionExecutedContext = new HttpActionExecutedContext(actionContext, null) { Response = response };
return actionExecutedContext;
}
public static HttpControllerDescriptor CreateControllerDescriptor(HttpConfiguration config = null)
{
if (config == null)
{
config = new HttpConfiguration();
}
return new HttpControllerDescriptor() { Configuration = config, ControllerName = "FooController" };
}
public static HttpActionDescriptor CreateActionDescriptor()
{
var mock = new Mock<HttpActionDescriptor>() { CallBase = true };
mock.SetupGet(d => d.ActionName).Returns("Bar");
return mock.Object;
}
}