-
Notifications
You must be signed in to change notification settings - Fork 1
Integration Tests
IOF edited this page Nov 28, 2025
·
3 revisions
public abstract class BaseIntegrationTest : IClassFixture<IntegrationTestApplicationFactory>
{
protected readonly ISender Sender;
protected readonly IMediator Mediator;
protected readonly ModuleBankAppContext ModuleBankAppContext;
protected BaseIntegrationTest(IntegrationTestApplicationFactory factory)
{
var scope = factory.Services.CreateScope();
Mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
Sender = scope.ServiceProvider.GetRequiredService<ISender>();
ModuleBankAppContext = scope.ServiceProvider.GetRequiredService<ModuleBankAppContext>();
}
}using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Testcontainers.PostgreSql;
using Xunit;
public class GameRepositoryIntegrationTests : IAsyncLifetime
{
private readonly PostgreSqlContainer _postgreSqlContainer;
private TicTacToeContext? _dbContext;
private GameAsyncRepository? _repository;
private Mock<ILogger<GameAsyncRepository>> _loggerMock;
public GameRepositoryIntegrationTests()
{
_postgreSqlContainer = new PostgreSqlBuilder()
.WithImage("postgres:latest")
.WithDatabase("testdb")
.WithUsername("postgres")
.WithPassword("postgres")
.Build();
_loggerMock = new Mock<ILogger<GameAsyncRepository>>();
}
public async Task InitializeAsync()
{
await _postgreSqlContainer.StartAsync();
var options = new DbContextOptionsBuilder<TicTacToeContext>()
.UseNpgsql(_postgreSqlContainer.GetConnectionString())
.Options;
_dbContext = new TicTacToeContext(options);
await _dbContext.Database.EnsureCreatedAsync();
// Заполняем тестовыми данными
for (int i = 0; i < 4; i++)
{
var game = new Game
{
Id = Guid.NewGuid(),
Status = StatusGame.Active,
Board = new string?[][]
{
},
Result = ResultGame.None,
CreatedAt = default,
CurrentMove = Player.X,
CurrentStep = 0
};
_dbContext.Games.Add(game);
}
await _dbContext.SaveChangesAsync();
_repository = new GameAsyncRepository(_dbContext, _loggerMock.Object);
}
public async Task DisposeAsync()
{
await _dbContext!.DisposeAsync();
await _postgreSqlContainer.DisposeAsync();
}
[Fact]
public async Task GetGamesAsync_ShouldReturn5GamesFromDatabase()
{
// Act
var games = await _repository!.GetGamesAsync(CancellationToken.None);
// Assert
Assert.Equal(5, games.Count());
}
}public sealed class IntegrationTestApplicationFactory : WebApplicationFactory<Program>, IAsyncLifetime
{
private readonly PostgreSqlContainer _dbContainer = new PostgreSqlBuilder()
.WithImage("postgres:latest")
.WithName("postgres_container_name")
.WithDatabase("testdb")
.WithUsername("postgres")
.WithPassword("root")
.Build();
private readonly RabbitMqContainer _rabbitMqContainer = new RabbitMqBuilder()
.WithImage("rabbitmq:3.12-management")
.WithName("rabbitmq_container_name")
.WithHostname("rabbitmq_test")
.Build();
public async Task InitializeAsync()
{
await _dbContainer.StartAsync();
await _rabbitMqContainer.StartAsync();
}
public new async Task DisposeAsync()
{
await _dbContainer.StopAsync();
await _rabbitMqContainer.StopAsync();
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
var descriptor = services.SingleOrDefault(
s => s.ServiceType == typeof(DbContextOptions<ModuleBankAppContext>));
if (descriptor != null) services.Remove(descriptor);
services.AddDbContext<ModuleBankAppContext>(options =>
options.UseNpgsql(_dbContainer.GetConnectionString()));
var hangfireDescriptor = services.FirstOrDefault(d =>
d.ServiceType == typeof(JobStorage));
if (hangfireDescriptor != null)
{
services.Remove(hangfireDescriptor);
}
services.AddHangfire(configuration => configuration
.SetDataCompatibilityLevel(CompatibilityLevel.Version_180)
.UseSimpleAssemblyNameTypeSerializer()
.UseRecommendedSerializerSettings()
.UsePostgreSqlStorage(c => c.UseNpgsqlConnection(_dbContainer.GetConnectionString())));
services.Configure<EventBusOptions>(o =>
{
o.HostName = _rabbitMqContainer.Hostname;
//o.Port = _rabbitMqContainer.GetMappedPublicPort(5672);
o.VirtualHost = "/";
o.ExchangeName = "account.events";
o.UserName = "guest";
o.Password = "guest";
});
var sp = services.BuildServiceProvider();
using var scope = sp.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<ModuleBankAppContext>();
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
});
}
}public class CreateAccountIntegrationTests : BaseIntegrationTest
{
private readonly ClaimsPrincipal _userWithValidId = new(
new ClaimsIdentity(
[new Claim(ClaimTypes.NameIdentifier, "a191ee39-08a7-4ffa-8f53-3c5f0f5f9b1c")]
)
);
private readonly ClaimsPrincipal _userWithoutId = new(new ClaimsIdentity());
private readonly CreateAccountDto _validAccountDto = new(
AccountType.Checking,
"USD",
1000m,
null
);
public CreateAccountIntegrationTests(IntegrationTestApplicationFactory factory)
: base(factory) { }
[Fact]
public async Task HandleRequest_WithInvalidUser_ReturnsUnauthorized()
{
// Act
var result = await CreateAccountEndpoint.HandleEndpoint(
_validAccountDto,
Mediator, // <- теперь настоящий Mediator из DI
_userWithoutId
);
// Assert
Assert.IsType<UnauthorizedHttpResult>(result);
}
[Fact]
public async Task HandleRequest_WhenMediatorReturnsFailure_ReturnsBadRequest()
{
// Здесь мы подсовываем невалидный DTO
var invalidDto = new CreateAccountDto(
AccountType.Deposit, // для депозита ставка обязательна
"USD",
1000m,
null
);
var result = await CreateAccountEndpoint.HandleEndpoint(
invalidDto,
Mediator,
_userWithValidId
);
var badRequest = Assert.IsType<BadRequest<MbResult<Account>>>(result);
Assert.False(badRequest.Value.IsSuccess);
// Assert.Contains("Interest rate is required", badRequest.Value.Error);
}
[Fact]
public async Task Handle_ShouldCreateAccount_WhenRequestIsValid()
{
// Arrange
var ownerId = Guid.NewGuid();
var request = new CreateAccountRequest(_validAccountDto, ownerId);
// Act
var result = await Sender.Send(request);
// Assert
Assert.True(result.IsSuccess);
Assert.NotNull(result.Value);
var account = result.Value;
Assert.Equal(AccountType.Checking, account.Type);
Assert.Equal("USD", account.Currency);
Assert.Equal(1000m, account.Balance);
Assert.Equal(ownerId, account.OwnerId);
// Проверим, что запись реально появилась в БД
var dbAccount = await ModuleBankAppContext.Accounts.FindAsync(account.Id);
Assert.NotNull(dbAccount);
Assert.Equal("USD", dbAccount!.Currency);
}
}