我想测试一些属于ASP.NET Web Api项目的类。我不需要通过TestServer进行请求-响应集成测试(尽管它们很好),但我想让我的测试尽可能接近“真实的东西”。因此,我希望使用Startup中添加的服务来解决我的类,但在测试的基础上通过stub/mock更改其中的一些(有些测试需要mock,而另一些则不需要)。
在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之外修改服务的方法。
发布于 2017-03-10 23:26:27
在通过创建自定义IHttpControllerActivator
配置HttpConfiguration时,可以为每个请求创建子范围。
这是针对OWIN的,但是转换到.Net核心应该非常简单:https://gist.github.com/jt000/eef096a2341471856e8a86d06aaec887
重要的部分是在该作用域中创建一个作用域和控制器……
var scope = _provider.CreateScope();
request.RegisterForDispose(scope);
var controller = scope.ServiceProvider.GetService(controllerType) as IHttpController;
...and正在覆盖默认的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 });
发布于 2018-01-02 16:59:33
如果您希望在xUnit
单元测试中使用相同的Web API Core
控制器和DI
基础设施(在本例中我将其称为集成测试),我建议将TestServer
和HttpClient
上下文移动到实现xUnit
IClassFixture
的基类中。
在这种情况下,您将使用在真实Web API Core
中配置的所有服务和DI
来测试API
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
服务:
public class MyServiceTests : TestServerDependent
{
public MyServiceTests(TestServerFixture fixture)
: base(fixture)
{
}
[Fact]
public void ReturnsDataWhenServiceInjected()
{
var service = GetService<IMyService>();
Assert.NotNull(service);
var data = service.GetData();
Assert.NotNull(data);
}
}
发布于 2017-03-01 16:51:50
这一切都在documentation中得到了很好的覆盖。
对于集成测试,您可以使用TestServer
类并给它一个Startup
类(不必是实时启动,也可以是StartupIntegrationTest
或startup with 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
/IServiceProvider
,只需模拟并注入接口即可。
https://stackoverflow.com/questions/42536916
复制