forked from ddrilling/AsbCloudServer
Интеграционные тесты
1. Добавил Docker Compose 2. Добавил AppFactory 3. Добавлен базовый класс для интеграционных классов 4. Покрыл тестами контроллер AdminDeposit
This commit is contained in:
parent
fd40a3b930
commit
2aa897d3df
@ -19,8 +19,10 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.25" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.1" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.1" />
|
||||||
<PackageReference Include="NSubstitute" Version="5.1.0" />
|
<PackageReference Include="NSubstitute" Version="5.1.0" />
|
||||||
|
<PackageReference Include="Testcontainers.PostgreSql" Version="3.6.0" />
|
||||||
<PackageReference Include="xunit" Version="2.4.2" />
|
<PackageReference Include="xunit" Version="2.4.2" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
47
AsbCloudWebApi.Tests/IntegrationTests/BaseIntegrationTest.cs
Normal file
47
AsbCloudWebApi.Tests/IntegrationTests/BaseIntegrationTest.cs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AsbCloudApp.Data.User;
|
||||||
|
using AsbCloudDb;
|
||||||
|
using AsbCloudDb.Model;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace AsbCloudWebApi.Tests.IntegrationTests;
|
||||||
|
|
||||||
|
public abstract class BaseIntegrationTest : IClassFixture<TestWebApplicationFactory>
|
||||||
|
{
|
||||||
|
private readonly IAsbCloudDbContext dbContext;
|
||||||
|
|
||||||
|
protected readonly HttpClient httpClient;
|
||||||
|
protected readonly IServiceScope scope;
|
||||||
|
|
||||||
|
protected BaseIntegrationTest(TestWebApplicationFactory factory)
|
||||||
|
{
|
||||||
|
scope = factory.Services.CreateScope();
|
||||||
|
httpClient = factory.CreateClient();
|
||||||
|
dbContext = scope.ServiceProvider.GetRequiredService<IAsbCloudDbContext>();
|
||||||
|
|
||||||
|
dbContext.Database.EnsureCreatedAndMigrated();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void SetToken(string token)
|
||||||
|
{
|
||||||
|
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: поправить метод регистрации, сделать этот метод следующим образом:
|
||||||
|
//1. Пройти регистрацию
|
||||||
|
//2. Залогиниться под новым логином и паролем. Тестить всё под учёткой dev -- это неправильно, но пока так.
|
||||||
|
// Пока используется jwt токен dev, пока так захардкожено
|
||||||
|
protected Task<UserTokenDto> RegisterUserAsync()
|
||||||
|
{
|
||||||
|
var authToken = new UserTokenDto
|
||||||
|
{
|
||||||
|
Token =
|
||||||
|
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjEiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiZGV2IiwiaWRDb21wYW55IjoiMSIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvcm9sZSI6InJvb3QiLCJuYmYiOjE3MDIzNjg3NzksImV4cCI6MTczMzkyNjM3OSwiaXNzIjoiYSIsImF1ZCI6ImEifQ.zMJHgoEkfifR28vIPxtABvznf8NFJjk33E9fxNZTwGM"
|
||||||
|
};
|
||||||
|
|
||||||
|
return Task.FromResult(authToken);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
using AsbCloudApp.Data;
|
||||||
|
using AsbCloudApp.Services;
|
||||||
|
|
||||||
|
namespace AsbCloudWebApi.Tests.IntegrationTests.Controllers;
|
||||||
|
|
||||||
|
public class AdminDepositControllerTests : CrudControllerTests<DepositDto, ICrudRepository<DepositDto>>
|
||||||
|
{
|
||||||
|
public AdminDepositControllerTests(TestWebApplicationFactory factory) : base(factory)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string Uri => "api/admin/deposit";
|
||||||
|
|
||||||
|
protected override DepositDto GetFakerData() => new()
|
||||||
|
{
|
||||||
|
Caption = "Fake deposit",
|
||||||
|
Timezone = new SimpleTimezoneDto
|
||||||
|
{
|
||||||
|
Hours = 5,
|
||||||
|
IsOverride = false,
|
||||||
|
TimezoneId = "Екатеринбург"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,275 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Json;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AsbCloudApp.Data;
|
||||||
|
using AsbCloudApp.Services;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace AsbCloudWebApi.Tests.IntegrationTests.Controllers;
|
||||||
|
|
||||||
|
public abstract class CrudControllerTests<T, TRepository> : BaseIntegrationTest
|
||||||
|
where T : class, IId
|
||||||
|
where TRepository : ICrudRepository<T>
|
||||||
|
{
|
||||||
|
private readonly TRepository repository;
|
||||||
|
|
||||||
|
protected abstract string Uri { get; }
|
||||||
|
|
||||||
|
protected abstract T GetFakerData();
|
||||||
|
|
||||||
|
protected CrudControllerTests(TestWebApplicationFactory factory)
|
||||||
|
: base(factory)
|
||||||
|
{
|
||||||
|
repository = scope.ServiceProvider.GetRequiredService<TRepository>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public virtual async Task InsertAsync_Should_ReturnsSuccess()
|
||||||
|
{
|
||||||
|
//arrange
|
||||||
|
var fakerData = GetFakerData();
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage
|
||||||
|
{
|
||||||
|
Content = JsonContent.Create(fakerData),
|
||||||
|
Method = HttpMethod.Post,
|
||||||
|
RequestUri = new Uri(Uri, UriKind.Relative)
|
||||||
|
};
|
||||||
|
|
||||||
|
var user = await RegisterUserAsync();
|
||||||
|
SetToken(user.Token);
|
||||||
|
|
||||||
|
//act
|
||||||
|
var response = await httpClient.SendAsync(request);
|
||||||
|
var result = await response.Content.ReadFromJsonAsync<int>();
|
||||||
|
|
||||||
|
//assert
|
||||||
|
Assert.True(result > 0);
|
||||||
|
Assert.True(response.IsSuccessStatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public virtual async Task InsertRangeAsync_Should_ReturnsSuccess()
|
||||||
|
{
|
||||||
|
//arrange
|
||||||
|
var fakerData = GetFakerData();
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage
|
||||||
|
{
|
||||||
|
Content = JsonContent.Create(new[] { fakerData }),
|
||||||
|
Method = HttpMethod.Post,
|
||||||
|
RequestUri = new Uri($"{Uri}/range", UriKind.Relative)
|
||||||
|
};
|
||||||
|
|
||||||
|
var user = await RegisterUserAsync();
|
||||||
|
SetToken(user.Token);
|
||||||
|
|
||||||
|
//act
|
||||||
|
var response = await httpClient.SendAsync(request);
|
||||||
|
var result = await response.Content.ReadFromJsonAsync<int>();
|
||||||
|
|
||||||
|
//assert
|
||||||
|
Assert.True(result > 0);
|
||||||
|
Assert.True(response.IsSuccessStatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public virtual async Task InsertRangeAsync_Should_ReturnsBadRequest_WhenInputCollectionFakerDataIsEmpty()
|
||||||
|
{
|
||||||
|
//arrange
|
||||||
|
var request = new HttpRequestMessage
|
||||||
|
{
|
||||||
|
Content = JsonContent.Create(Enumerable.Empty<T>()),
|
||||||
|
Method = HttpMethod.Post,
|
||||||
|
RequestUri = new Uri($"{Uri}/range", UriKind.Relative)
|
||||||
|
};
|
||||||
|
|
||||||
|
var user = await RegisterUserAsync();
|
||||||
|
SetToken(user.Token);
|
||||||
|
|
||||||
|
//act
|
||||||
|
var response = await httpClient.SendAsync(request);
|
||||||
|
|
||||||
|
//assert
|
||||||
|
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public virtual async Task UpdateAsync_Should_ReturnsSuccess()
|
||||||
|
{
|
||||||
|
//arrange
|
||||||
|
var fakerData = GetFakerData();
|
||||||
|
fakerData.Id = await repository.InsertAsync(fakerData, default);
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage
|
||||||
|
{
|
||||||
|
Content = JsonContent.Create(fakerData),
|
||||||
|
Method = HttpMethod.Put,
|
||||||
|
RequestUri = new Uri(Uri, UriKind.Relative)
|
||||||
|
};
|
||||||
|
|
||||||
|
var user = await RegisterUserAsync();
|
||||||
|
SetToken(user.Token);
|
||||||
|
|
||||||
|
//act
|
||||||
|
var response = await httpClient.SendAsync(request);
|
||||||
|
var result = await response.Content.ReadFromJsonAsync<int>();
|
||||||
|
|
||||||
|
//assert
|
||||||
|
Assert.Equal(fakerData.Id, result);
|
||||||
|
Assert.True(response.IsSuccessStatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public virtual async Task UpdateAsync_Should_ReturnsBadRequest_WhenFakerDataHasInvalidId()
|
||||||
|
{
|
||||||
|
//arrange
|
||||||
|
var fakerData = GetFakerData();
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage
|
||||||
|
{
|
||||||
|
Content = JsonContent.Create(fakerData),
|
||||||
|
Method = HttpMethod.Put,
|
||||||
|
RequestUri = new Uri(Uri, UriKind.Relative)
|
||||||
|
};
|
||||||
|
|
||||||
|
var user = await RegisterUserAsync();
|
||||||
|
SetToken(user.Token);
|
||||||
|
|
||||||
|
//act
|
||||||
|
var response = await httpClient.SendAsync(request);
|
||||||
|
|
||||||
|
//assert
|
||||||
|
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public virtual async Task DeleteAsync_Should_ReturnsSuccess()
|
||||||
|
{
|
||||||
|
//arrange
|
||||||
|
var fakerData = GetFakerData();
|
||||||
|
fakerData.Id = await repository.InsertAsync(fakerData, default);
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage
|
||||||
|
{
|
||||||
|
Method = HttpMethod.Delete,
|
||||||
|
RequestUri = new Uri($"{Uri}/{fakerData.Id}", UriKind.Relative)
|
||||||
|
};
|
||||||
|
|
||||||
|
var user = await RegisterUserAsync();
|
||||||
|
SetToken(user.Token);
|
||||||
|
|
||||||
|
//act
|
||||||
|
var response = await httpClient.SendAsync(request);
|
||||||
|
var result = await response.Content.ReadFromJsonAsync<int>();
|
||||||
|
|
||||||
|
//assert
|
||||||
|
Assert.True(result > 0);
|
||||||
|
Assert.True(response.IsSuccessStatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public virtual async Task DeleteAsync_Should_ReturnsNoContent_WhenFakerDataHasInvalidId()
|
||||||
|
{
|
||||||
|
//arrange
|
||||||
|
const int fakerInvalidId = 0;
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage
|
||||||
|
{
|
||||||
|
Method = HttpMethod.Delete,
|
||||||
|
RequestUri = new Uri($"{Uri}/{fakerInvalidId}", UriKind.Relative)
|
||||||
|
};
|
||||||
|
|
||||||
|
var user = await RegisterUserAsync();
|
||||||
|
SetToken(user.Token);
|
||||||
|
|
||||||
|
//act
|
||||||
|
var response = await httpClient.SendAsync(request);
|
||||||
|
|
||||||
|
//assert
|
||||||
|
Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public virtual async Task GetOrDefaultAsync_Should_ReturnsSuccess()
|
||||||
|
{
|
||||||
|
//arrange
|
||||||
|
var fakerData = GetFakerData();
|
||||||
|
fakerData.Id = await repository.InsertAsync(fakerData, default);
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage
|
||||||
|
{
|
||||||
|
Method = HttpMethod.Get,
|
||||||
|
RequestUri = new Uri($"{Uri}/{fakerData.Id}", UriKind.Relative)
|
||||||
|
};
|
||||||
|
|
||||||
|
var user = await RegisterUserAsync();
|
||||||
|
SetToken(user.Token);
|
||||||
|
|
||||||
|
//act
|
||||||
|
var response = await httpClient.SendAsync(request);
|
||||||
|
var result = await response.Content.ReadFromJsonAsync<T>();
|
||||||
|
|
||||||
|
//assert
|
||||||
|
Assert.NotNull(result);
|
||||||
|
Assert.Equal(fakerData.Id, result.Id);
|
||||||
|
Assert.True(response.IsSuccessStatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public virtual async Task GetOrDefaultAsync_Should_ReturnsNoContent_WhenFakerDataHasInvalidId()
|
||||||
|
{
|
||||||
|
//arrange
|
||||||
|
const int fakerInvalidId = 0;
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage
|
||||||
|
{
|
||||||
|
Method = HttpMethod.Get,
|
||||||
|
RequestUri = new Uri($"{Uri}/{fakerInvalidId}", UriKind.Relative)
|
||||||
|
};
|
||||||
|
|
||||||
|
var user = await RegisterUserAsync();
|
||||||
|
SetToken(user.Token);
|
||||||
|
|
||||||
|
//act
|
||||||
|
var response = await httpClient.SendAsync(request);
|
||||||
|
|
||||||
|
//assert
|
||||||
|
Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public virtual async Task GetAllAsync_Should_ReturnsSuccess()
|
||||||
|
{
|
||||||
|
//arrange
|
||||||
|
var existingFakerDataIds = (await repository.GetAllAsync(default)).Select(f => f.Id);
|
||||||
|
|
||||||
|
foreach (var existingFakerDataId in existingFakerDataIds)
|
||||||
|
await repository.DeleteAsync(existingFakerDataId, default);
|
||||||
|
|
||||||
|
var fakerData = GetFakerData();
|
||||||
|
fakerData.Id = await repository.InsertAsync(fakerData, default);
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage
|
||||||
|
{
|
||||||
|
Method = HttpMethod.Get,
|
||||||
|
RequestUri = new Uri(Uri, UriKind.Relative)
|
||||||
|
};
|
||||||
|
|
||||||
|
var user = await RegisterUserAsync();
|
||||||
|
SetToken(user.Token);
|
||||||
|
|
||||||
|
//act
|
||||||
|
var response = await httpClient.SendAsync(request);
|
||||||
|
var result = await response.Content.ReadFromJsonAsync<T[]>();
|
||||||
|
|
||||||
|
//assert
|
||||||
|
Assert.NotNull(result);
|
||||||
|
Assert.Single(result);
|
||||||
|
Assert.True(response.IsSuccessStatusCode);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AsbCloudDb.Model;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Testing;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Testcontainers.PostgreSql;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace AsbCloudWebApi.Tests.IntegrationTests;
|
||||||
|
|
||||||
|
public class TestWebApplicationFactory : WebApplicationFactory<Startup>,
|
||||||
|
IAsyncLifetime
|
||||||
|
{
|
||||||
|
private readonly PostgreSqlContainer _dbContainer = new PostgreSqlBuilder()
|
||||||
|
.WithImage("timescale/timescaledb:latest-pg15")
|
||||||
|
.WithDatabase("AsbCloud")
|
||||||
|
.WithUsername("postgres")
|
||||||
|
.WithPassword("postgres")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
protected override void ConfigureWebHost(IWebHostBuilder builder)
|
||||||
|
{
|
||||||
|
builder.ConfigureServices(services =>
|
||||||
|
{
|
||||||
|
var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<AsbCloudDbContext>));
|
||||||
|
|
||||||
|
if (descriptor is not null)
|
||||||
|
services.Remove(descriptor);
|
||||||
|
|
||||||
|
services.AddDbContext<AsbCloudDbContext>(options =>
|
||||||
|
options.UseNpgsql(_dbContainer.GetConnectionString()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task InitializeAsync()
|
||||||
|
{
|
||||||
|
return _dbContainer.StartAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public new Task DisposeAsync()
|
||||||
|
{
|
||||||
|
return _dbContainer.StopAsync();
|
||||||
|
}
|
||||||
|
}
|
24
docker-compose.yml
Normal file
24
docker-compose.yml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
version: '3.9'
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
postgres:
|
||||||
|
container_name: pg_db
|
||||||
|
image: timescale/timescaledb:latest-pg15
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
POSTGRES_DB: "AsbCloud"
|
||||||
|
volumes:
|
||||||
|
- ./timescaledb_data:/var/lib/postgresql/data
|
||||||
|
ports:
|
||||||
|
- "5434:5432"
|
||||||
|
|
||||||
|
pgadmin:
|
||||||
|
container_name: pgadmin
|
||||||
|
image: dpage/pgadmin4
|
||||||
|
environment:
|
||||||
|
PGADMIN_DEFAULT_EMAIL: noemail@noemail.com
|
||||||
|
PGADMIN_DEFAULT_PASSWORD: root
|
||||||
|
ports:
|
||||||
|
- "5050:80"
|
Loading…
Reference in New Issue
Block a user