我正在进行以下设置:
public class ExampleBaseController : Microsoft.AspNetCore.Mvc.Controller
{
public UserDetails UserDetails => Request.GetUserDetailsFromHttpHeaders();
}
public class ExampleConcreteController : ExampleBaseController
{
// UserDetails is being used in here
// this is the class under test
我需要能够在生产运行期间注入UserDetails,还必须能够在测试期间对其进行模拟。 由于UserDetails依赖于Request,并且Request是Microsoft.AspNetCore.Mvc.Controller的成员,所以我不知道该如何实现。
答案 0 :(得分:2)
如果您想模拟某些东西,则应首先允许模拟。如果要模拟UserDetails,则应允许在其getter上进行模拟,并在新建合同中传递所需的上下文:
public class ExampleBaseController : Microsoft.AspNetCore.Mvc.Controller
{
private readonly IUserDetailsProvider _userDetailsProvider;
public UserDetails UserDetails => _userDetailsProvider.Get(Request);
public ExampleBaseController(IUserDetailsProvider userDetailsProvider)
{
_userDetailsProvider = userDetailsProvider;
}
}
因此,在测试中,您模拟IUserDetailsProvider
以返回一些“ foobar”。在生产环境中,您只需调用GetUserDetailsFromHttpHeaders()
内部传递的Request
方法。
回答有关请求和控制器关系的问题。 Controller取决于Request,是的,Microsoft认为将它们牢固地合并在一起而不是传递依赖关系将是一件好事,例如:
public class FooBarController : Microsoft.AspNetCore.Mvc.Controller
{
private readonly System.Web.HttpRequestBase _request;
public FooBarController(System.Web.HttpRequestBase request)
{
_request = request;
}
}
甚至是这样:
public class FooBarController : Microsoft.AspNetCore.Mvc.Controller
{
public void ProcessRequest(System.Web.HttpRequestBase request)
{
//request here
}
}
他们改为使用属性注入,这使开发人员无法影响注入。这是个问题。但并非无法解决-如果您需要将那些耦合在一起的对象之一,只需在内部传递上下文(按委托,按接口,按引用)。
答案 1 :(得分:0)
@eocron提出的解决方案可能不那么方便,但仍然:
public interface IWithUserDetails
{
UserDetails UserDetails();
}
public class ExampleBaseController : Microsoft.AspNetCore.Mvc.Controller, IWithUserDetails
{
public UserDetails UserDetails()
{
return Request.GetUserDetailsFromHttpHeaders();
}
}
使用相同的类和方法名称并不是最好的方法,但就像带有属性的示例中一样
答案 2 :(得分:0)
这是另一种观点:
public interface IUserDetailsProviderOptions
{
Func<UserDetails> UserDetailsProvider { get; set; }
}
public class DefaultUserDetailsProviderOptions : IUserDetailsProviderOptions
{
public Func<UserDetails> UserDetailsProvider {get; set;}
}
public class ExampleBaseController : Microsoft.AspNetCore.Mvc.Controller
{
private readonly Func<UserDetails> _userDetailsProvider;
public UserDetails UserDetails => _userDetailsProvider();
public ExampleBaseController(IUserDetailsProviderOptions options)
{
_userDetailsProvider = options.UserDetailsProvider ??
Request.GetUserDetailsFromHttpHeaders;
}
}
像这样在Startup.cs中注册:
services.AddSingleton<IUserDetailsProviderOptions, DefaultUserDetailsProviderOptions>();
在测试中,您可以这样做:
public class StubUserDetailsOption : IUserDetailsProviderOptions
{
public Func<UserDetails> UserDetailsProvider { get; set; } = () => new StubDetails();
}
var controller = new ExampleBaseController(new StubUserDetailsOption());
并进行测试。