我想测试一些属于ASP.NET Web Api项目的类。我不需要通过TestServer进行请求 - 响应集成测试(虽然它们很好)但我想让我的测试尽可能接近“真实的东西”。所以我想使用Startup中添加的服务来解析我的类,但是在测试的基础上通过存根/模拟更改其中的一些(一些测试需要模拟,其他测试需要 - 但不要)。
在ASP.NET没有内部依赖注入框架的过去,这很容易做到。所以我只调用一个将所有依赖项注册到容器的类,然后为每个测试创建子容器,将一些依赖项更改为mocks,就是这样。
我试过这样的事情:
var host = A.Fake<IHostingEnvironment>();
var startup = new Startup(host);
var services = new ServiceCollection();
//Add stubs here
startup.ConfigureServices(services);
var provider = services.BuildServiceProvider();
provider.GetService<IClientsHandler>();
它似乎有效,但我不想为每个测试创建整个启动基础架构。我想创建它一次,然后为每个测试创建“子容器”或“子范围”。可能吗?基本上我正在寻找一种方法来修改Startup以外的服务。
答案 0 :(得分:4)
如果您希望为npm install
单元测试使用相同的Web API Core
控制器和DI
基础结构(在这种情况下我称之为集成测试),我建议将xUnit
和TestServer
上下文移动到实现HttpClient
IClassFixture
的基类。
在这种情况下,您将使用所有服务测试xUnit
并在您的真实API
中配置DI
:
Web API Core
然后你可以从这个类派生出来以这种方式测试控制器动作:
public class TestServerDependent : IClassFixture<TestServerFixture>
{
private readonly TestServerFixture _fixture;
public TestServer TestServer => _fixture.Server;
public HttpClient Client => _fixture.Client;
public TestServerDependent(TestServerFixture fixture)
{
_fixture = fixture;
}
protected TService GetService<TService>()
where TService : class
{
return _fixture.GetService<TService>();
}
}
public class TestServerFixture : IDisposable
{
public TestServer Server { get; }
public HttpClient Client { get; }
public TestServerFixture()
{
// UseStaticRegistration is needed to workaround AutoMapper double initialization. Remove if you don't use AutoMapper.
ServiceCollectionExtensions.UseStaticRegistration = false;
var hostBuilder = new WebHostBuilder()
.UseEnvironment("Testing")
.UseStartup<Startup>();
Server = new TestServer(hostBuilder);
Client = Server.CreateClient();
}
public void Dispose()
{
Server.Dispose();
Client.Dispose();
}
public TService GetService<TService>()
where TService : class
{
return Server?.Host?.Services?.GetService(typeof(TService)) as TService;
}
}
您还可以测试public class ValueControllerTests : TestServerDependent
{
public ValueControllerTests(TestServerFixture fixture)
: base(fixture)
{
}
[Fact]
public void Returns_Ok_Response_When_Requested()
{
var responseMessage = Client.GetAsync("/api/value").Result;
Assert.Equal(HttpStatusCode.OK, responseMessage.StatusCode);
}
}
服务:
DI
答案 1 :(得分:3)
通过创建自定义IHttpControllerActivator
来配置HttpConfiguration时,可以为每个请求创建子范围。
这适用于OWIN,但转换为.Net Core应该非常简单: https://gist.github.com/jt000/eef096a2341471856e8a86d06aaec887
重要的部分是用于创建Scope&amp;控制器在那个范围内......
var scope = _provider.CreateScope();
request.RegisterForDispose(scope);
var controller = scope.ServiceProvider.GetService(controllerType) as IHttpController;
...并覆盖默认的IHttpControllerActivator
...
config.Services.Replace(typeof (IHttpControllerActivator), new ServiceProviderControllerActivator(parentActivator, provider));
现在您可以通过带有作用域依赖注入的IServiceProvider添加要创建的控制器...
services.AddScoped<ValuesController>((sp) => new ValuesController(sp.GetService<ISomeCustomService>()));
要在单元测试中测试ValuesController,我建议使用类似Moq框架的东西来模拟服务接口中的方法。例如:
var someCustomService = Mock.Of<ISomeCustomService>(s => s.DoSomething() == 3);
var sut = new ValuesController(someCustomService);
var result = sut.Get();
Assert.AreEqual(result, new [] { 3 });
答案 2 :(得分:2)
documentation中包含了所有内容。
对于集成测试,您使用TestServer
类并为其提供Startup
类(不必是实时启动,也可以是StartupIntegrationTest
或启动{{1} } / Configure{Envrionment Name here}
方法。
ConfigureServices{Envrionment Name here}
要访问服务提供商,请执行
var server = new TestServer(new WebHostBuilder()
.UseStartup<Startup>()
// this would cause it to use StartupIntegrationTest class or ConfigureServicesIntegrationTest / ConfigureIntegrationTest methods (if existing)
// rather than Startup, ConfigureServices and Configure
.UseEnvironment("IntegrationTest"));
对于单元测试,你根本不应该使用var server = new TestServer(new WebHostBuilder()
.UseStartup<Startup>()
.UseEnvironment("IntegrationTest"));
var controller = server.Host.Services.GetService<MyService>();
/ IServiceCollection
,只需模拟接口并注入它们。