我有一个ASP.NET核心网络应用程序和测试设置使用WebApplicationFactory测试我的控制器操作。我以前使用过RawRabbit,很容易模拟IBusClient并将其作为单例添加到DI容器中。在WebApplicationFactory<TStartup>.CreateWebHostBuilder()中,我调用这个扩展方法来添加模拟的IBusClient实例,如下所示;
/// <summary>
/// Configures the service bus.
/// </summary>
/// <param name="webHostBuilder">The web host builder.</param>
/// <returns>A web host builder.</returns>
public static IWebHostBuilder ConfigureTestServiceBus(this IWebHostBuilder webHostBuilder)
{
webHostBuilder.ConfigureTestServices(services =>
{
services.AddSingleton<IBusClient, MY_MOCK_INSTANCE>
});
return webHostBuilder;
}但是,现在RawRabbit中存在一些空白,这使得我决定转到MassTransit。但是,我想知道是否已经有更好的方法将IBus注册到我的容器中,而不用在测试中模拟它。不确定InMemoryTestFixture、BusTestFixture或BusTestHarness是否解决了我的问题。不知道如何在一起使用它们以及它们是做什么的。
顺便说一句,在我的ASP.NET核心应用程序中,我设置了一个可重用的扩展方法,就像下面的代码一样,可以在启动时将我连接到RabbitMQ。
/// <summary>
/// Adds the service bus.
/// </summary>
/// <param name="services">The services.</param>
/// <param name="configurator">The configurator.</param>
/// <returns>A service collection.</returns>
public static IServiceCollection AddServiceBus(this IServiceCollection services, Action<IServiceCollectionConfigurator> configurator)
{
var rabbitMqConfig = new ConfigurationBuilder()
.AddJsonFile("/app/configs/service-bus.json", optional: false, reloadOnChange: true)
.Build();
// Setup DI for MassTransit.
services.AddMassTransit(x =>
{
configurator(x);
// Get the json configuration and use it to setup connection to RabbitMQ.
var rabbitMQConfig = rabbitMqConfig.GetSection(ServiceBusOptionsKey).Get<RabbitMQOptions>();
// Add bus to the container.
x.AddBus(provider => Bus.Factory.CreateUsingRabbitMq(cfg =>
{
cfg.Host(
new Uri(rabbitMQConfig.Host),
hostConfig =>
{
hostConfig.Username(rabbitMQConfig.Username);
hostConfig.Password(rabbitMQConfig.Password);
hostConfig.Heartbeat(rabbitMQConfig.Heartbeat);
});
cfg.ConfigureEndpoints(provider);
// Add Serilog logging.
cfg.UseSerilog();
}));
});
// Add the hosted service that starts and stops the BusControl.
services.AddSingleton<IMessageDataRepository, EncryptedMessageDataRepository>();
services.AddSingleton<IEndpointNameFormatter, EndpointNameFormatter>();
services.AddSingleton<IBus>(provider => provider.GetRequiredService<IBusControl>());
services.AddSingleton<IHostedService, BusHostedService>();
return services;
}发布于 2020-06-08 04:32:47
最后,我在我的WebApplicationFactory中创建了一个方法,如下所示;
public void ConfigureTestServiceBus(Action<IServiceCollectionConfigurator> configurator)
{
this._configurator = configurator;
}使我能够在派生的集成类构造函数中注册测试处理程序;
public Intg_GetCustomers(WebApplicationTestFactory<Startup> factory)
: base(factory)
{
factory.ConfigureTestServiceBus(c =>
{
c.AddConsumer<TestGetProductConsumer>();
});
}当我调用扩展方法来添加InMemory实例MassTransit时,就会使用这个配置器
public static IWebHostBuilder ConfigureTestServiceBus(this IWebHostBuilder webHostBuilder, Action<IServiceCollectionConfigurator> configurator)
{
return webHostBuilder
.ConfigureTestServices(services =>
{
// UseInMemoryServiceBus DI for MassTransit.
services.AddMassTransit(c =>
{
configurator?.Invoke(c);
// Add bus to the container.
c.AddBus(provider =>
{
var control = Bus.Factory.CreateUsingInMemory(cfg =>
{
cfg.ConfigureEndpoints(provider);
});
control.Start();
return control;
});
});
services.AddSingleton<IMessageDataRepository, InMemoryMessageDataRepository>();
});
}发布于 2021-07-29 22:42:34
启动期间定义的MassTransit配置可以通过从MassTransit命名空间中删除服务来替换为具有自定义WebApplicationFactory的新配置。
public class CustomWebApplicationFactory : WebApplicationFactory<Startup>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
var massTransitHostedService = services.FirstOrDefault(d => d.ServiceType == typeof(IHostedService) &&
d.ImplementationFactory != null &&
d.ImplementationFactory.Method.ReturnType == typeof(MassTransitHostedService)
);
services.Remove(massTransitHostedService);
var descriptors = services.Where(d =>
d.ServiceType.Namespace.Contains("MassTransit",StringComparison.OrdinalIgnoreCase))
.ToList();
foreach (var d in descriptors)
{
services.Remove(d);
}
services.AddMassTransitInMemoryTestHarness(x =>
{
//add your consumers (again)
});
});
}
}那么你的测试看起来就像
public class TestClass : IClassFixture<CustomApplicationFactory>
{
private readonly CustomApplicationFactoryfactory;
public TestClass(CustomApplicationFactoryfactory)
{
this.factory = factory;
}
[Fact]
public async Task TestName()
{
CancellationToken cancellationToken = new CancellationTokenSource(5000).Token;
var harness = factory.Services.GetRequiredService<InMemoryTestHarness>();
await harness.Start();
var bus = factory.Services.GetRequiredService<IBusControl>();
try
{
await bus.Publish<MessageClass>(...some message...);
bool consumed = await harness.Consumed.Any<MessageClass>(cancellationToken);
//do your asserts
}
finally
{
await harness.Stop();
}
}
}发布于 2019-08-18 17:13:12
最好的选择是使用InMemoryTestHarness,这样您就可以确保您的消息契约能够被序列化,您的消费者被正确地配置,并且一切都按预期工作。虽然有些人可能把这称为集成测试,但它实际上只是在做一个适当的测试。而且速度非常快,因为它都是记忆中的。
您可以看到一个单元测试这里,但下面也显示了一个简短的示例。
[TestFixture]
public class When_a_consumer_is_being_tested
{
InMemoryTestHarness _harness;
ConsumerTestHarness<Testsumer> _consumer;
[OneTimeSetUp]
public async Task A_consumer_is_being_tested()
{
_harness = new InMemoryTestHarness();
_consumer = _harness.Consumer<Testsumer>();
await _harness.Start();
await _harness.InputQueueSendEndpoint.Send(new A());
}
[OneTimeTearDown]
public async Task Teardown()
{
await _harness.Stop();
}
[Test]
public void Should_have_called_the_consumer_method()
{
_consumer.Consumed.Select<A>().Any().ShouldBe(true);
}
class Testsumer :
IConsumer<A>
{
public async Task Consume(ConsumeContext<A> context)
{
await context.RespondAsync(new B());
}
}
class A
{
}
class B
{
}
}https://stackoverflow.com/questions/57541541
复制相似问题