{"id":482687,"date":"2026-06-07T09:53:17","date_gmt":"2026-06-07T09:53:17","guid":{"rendered":"https:\/\/savepearlharbor.com\/?p=482687"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=482687","title":{"rendered":"\u041a\u0430\u043a \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u044c \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 ASP.NET Core API"},"content":{"rendered":"<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/fc0\/377\/38c\/fc037738c7ff4136d8da91a920c0bde7.png\" width=\"897\" height=\"500\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/fc0\/377\/38c\/fc037738c7ff4136d8da91a920c0bde7.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/fc0\/377\/38c\/fc037738c7ff4136d8da91a920c0bde7.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0420\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u043c\u044b \u0441\u0442\u0430\u0440\u0430\u0435\u043c\u0441\u044f \u043d\u0435 \u0437\u043b\u043e\u0443\u043f\u043e\u0442\u0440\u0435\u0431\u043b\u044f\u0442\u044c \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043a\u043e\u0434\u0430. \u0418\u0437 \u0447\u0430\u0441\u0442\u043e \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u044e\u0449\u0435\u0433\u043e\u0441\u044f \u043a\u043e\u0434\u0430 \u043c\u044b \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438, \u0430 \u0434\u043b\u044f \u0438\u0445 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0432 <em>\u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0435 ASP.NET Core \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f<\/em> \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c DI-\u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440.<br \/><em>\u0418\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f<\/em> \u0434\u043b\u044f ASP.NET Core API, \u043a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u043e, \u0442\u043e\u0436\u0435 \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u0435\u0442\u0441\u044f, \u043d\u043e \u043a\u0430\u043a\u0438\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u043f\u043e\u043c\u043e\u0433\u0430\u044e\u0442 \u043d\u0430\u043c \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u043a\u043e\u0434?<br \/>\u0420\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 <code>Python<\/code> \u0440\u0435\u0448\u0430\u044e\u0442 \u044d\u0442\u0443 \u0437\u0430\u0434\u0430\u0447\u0443 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>pytest.fixtures<\/code>, \u043e\u0434\u043d\u0430\u043a\u043e \u0432 <code>dotnet<\/code>-\u044d\u043a\u043e\u0441\u0438\u0441\u0442\u0435\u043c\u0435 (<code>xUnit<\/code>) \u0445\u043e\u0440\u043e\u0448\u0435\u0433\u043e \u0430\u043d\u0430\u043b\u043e\u0433\u0430 \u043f\u043e\u043a\u0430 \u043d\u0435\u0442.<br \/>\u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043f\u0440\u0438\u043c\u0435\u0440, \u043a\u0430\u043a \u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0440\u043e\u043a \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u043e\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435 \u0441 \u0438\u0437\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0439 \u0411\u0414, \u0444\u0435\u0439\u043a\u043e\u0432\u044b\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c \u0438 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0441\u0442\u044c\u044e, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043a\u0430\u043a \u0434\u043e\u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u044d\u0442\u043e \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u0432\u0437\u044f\u0442\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430.<\/p>\n<p><strong>\u0414\u043b\u044f \u043a\u043e\u0433\u043e \u044d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f:<\/strong> \u0434\u043b\u044f \u0431\u044d\u043a\u0435\u043d\u0434-\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 (\u043d\u0430 .NET), \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u043b\u0438\u0434\u043e\u0432, QA-\u0438\u043d\u0436\u0435\u043d\u0435\u0440\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0438\u0448\u0443\u0442 \u043a\u043e\u0434, \u0438 \u0432\u0441\u0435\u0445, \u043a\u0442\u043e \u0443\u0441\u0442\u0430\u043b \u043e\u0442 \u0431\u0435\u0441\u0441\u043c\u044b\u0441\u043b\u0435\u043d\u043d\u043e \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u044e\u0449\u0435\u0433\u043e\u0441\u044f \u043a\u043e\u0434\u0430 \u0432 \u0442\u0435\u0441\u0442\u0430\u0445.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u043f\u0438\u0448\u0435\u0442\u0435 \u043d\u0430 C#, \u043d\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u0435\u0441\u0442\u0430\u043c \u044d\u043b\u0435\u0433\u0430\u043d\u0442\u043d\u043e\u0441\u0442\u0438 Python \u2014 \u0434\u043e\u0431\u0440\u043e \u043f\u043e\u0436\u0430\u043b\u043e\u0432\u0430\u0442\u044c.<\/p>\n<h3>\u0421\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435<\/h3>\n<ul>\n<li>\n<p><a href=\"#%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5\" rel=\"noopener noreferrer nofollow\">\u0422\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 (WeatherForecast API)<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#%D0%BF%D1%80%D0%BE%D0%B1%D0%BB%D0%B5%D0%BC%D1%8B\" rel=\"noopener noreferrer nofollow\">\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0441\u043f\u043e\u0441\u043e\u0431\u0430 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5\" rel=\"noopener noreferrer nofollow\">\u0420\u0435\u0448\u0435\u043d\u0438\u0435: \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u044b \u043a\u0430\u043a \u0441\u043f\u043e\u0441\u043e\u0431 \u043c\u044b\u0448\u043b\u0435\u043d\u0438\u044f<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#%D0%BF%D1%80%D0%B0%D0%BA%D1%82%D0%B8%D0%BA%D0%B0\" rel=\"noopener noreferrer nofollow\">\u041f\u0440\u0430\u043a\u0442\u0438\u043a\u0430: \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c WeatherForecast API<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#%D1%81%D1%80%D0%B0%D0%B2%D0%BD%D0%B5%D0%BD%D0%B8%D0%B5\" rel=\"noopener noreferrer nofollow\">\u0421\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0435 \u0441 \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430\u043c\u0438<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#%D0%B7%D0%B0%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B5\" rel=\"noopener noreferrer nofollow\">\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#%D1%81%D1%81%D1%8B%D0%BB%D0%BA%D0%B8\" rel=\"noopener noreferrer nofollow\">\u0421\u0441\u044b\u043b\u043a\u0438 \u0438 \u0440\u0435\u0441\u0443\u0440\u0441\u044b<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#%D0%B1%D0%BE%D0%BD%D1%83%D1%81\" rel=\"noopener noreferrer nofollow\">\u0411\u043e\u043d\u0443\u0441<\/a><\/p>\n<\/li>\n<\/ul>\n<p>\u0421\u0442\u0430\u0442\u044c\u044f \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u0430\u043d\u0430 \u043d\u0430 \u0447\u0438\u0442\u0430\u0442\u0435\u043b\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432 \u0446\u0435\u043b\u043e\u043c \u043f\u043e\u043d\u0438\u043c\u0430\u044e\u0442, \u043a\u0442\u043e, \u043a\u0430\u043a \u0438 \u0437\u0430\u0447\u0435\u043c \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u0442 \u0431\u044d\u043a\u0435\u043d\u0434.<\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u043f\u043e\u0439\u043b\u0435\u0440<\/summary>\n<div class=\"spoiler__content\">\n<p> \u041f\u043e-\u0445\u043e\u0440\u043e\u0448\u0435\u043c\u0443 \u0441\u0442\u043e\u0438\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0443\u044e \u0441\u0442\u0430\u0442\u044c\u044e \u0438 \u043d\u0430 \u044d\u0442\u0443 \u0442\u0435\u043c\u0443. <\/p>\n<\/div>\n<\/details>\n<p><a class=\"anchor\" name=\"%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5\" id=\"\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\"><\/a><\/p>\n<h3>\u0422\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 (WeatherForecast API)<\/h3>\n<p>\u041d\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u2014 minimal ASP.NET Core API \u0441 \u0434\u0432\u0443\u043c\u044f \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430\u043c\u0438:<\/p>\n<ul>\n<li>\n<p><code>POST \/weatherforecast\/generate<\/code> \u2014 \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u043f\u0440\u043e\u0433\u043d\u043e\u0437 \u043f\u043e\u0433\u043e\u0434\u044b, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f <code>TimeProvider<\/code>, <code>Random<\/code> \u0438 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, <code>appsettings.json<\/code>), \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442 \u0435\u0433\u043e \u0432 PostgreSQL \u0447\u0435\u0440\u0435\u0437 EF Core.<\/p>\n<\/li>\n<li>\n<p><code>GET \/weatherforecast\/today<\/code> \u2014 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043f\u0440\u043e\u0433\u043d\u043e\u0437 \u043d\u0430 \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u0438\u0437 \u0411\u0414.<\/p>\n<\/li>\n<\/ul>\n<details class=\"spoiler\">\n<summary>\u041f\u043e\u043b\u043d\u044b\u0439 \u043a\u043e\u0434 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f<\/summary>\n<div class=\"spoiler__content\">\n<p>\u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0430 \u0411\u0414 <code>PostgreSQL<\/code>, \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430\u044f \u0447\u0435\u0440\u0435\u0437 <code>ConnectionString<\/code> \u0441 \u0438\u043c\u0435\u043d\u0435\u043c <code>PgDb<\/code>.<\/p>\n<pre><code class=\"cs\">using Microsoft.EntityFrameworkCore;namespace WebApiTestSubject;public class Program{    public const string ConnectionStringName = \"PgDb\";    public static void Main(string[] args)    {        var builder = WebApplication.CreateBuilder(args);        builder.Services            .AddSingleton((_) =&gt; Random.Shared)            .AddSingleton((_) =&gt; TimeProvider.System)            .AddDbContext&lt;ApplicationDbContext&gt;((sp, options) =&gt;            {                var connStr = builder.Configuration.GetConnectionString(ConnectionStringName);                options.UseNpgsql(connStr);            });        var app = builder.Build();        app.MapPost(\"\/weatherforecast\/generate\", async (TimeProvider tp, Random r, IConfiguration cfg, ApplicationDbContext dbCtx) =&gt;        {            var now = tp.GetUtcNow();            var date = DateOnly.FromDateTime(now.Date);            var temperature = r.Next(100);            var summary = cfg.GetValue&lt;string&gt;(\"summary\");            var forecast = new WeatherForecast(date, temperature, summary);            dbCtx.WeatherForecasts.Add(new WeatherForecastEntity { Data = forecast });                        await dbCtx.SaveChangesAsync();        });        app.MapGet(\"\/weatherforecast\/today\", async (TimeProvider tp, ApplicationDbContext dbCtx) =&gt;        {            var now = tp.GetUtcNow();            var today = DateOnly.FromDateTime(now.Date);            var entity = await dbCtx.WeatherForecasts                .Where(x =&gt; x.Data.Date == today)                .FirstOrDefaultAsync();                            return entity is null ? Results.NotFound() : Results.Ok(entity.Data);        });        app.Run();    }}public class ApplicationDbContext : DbContext{    public DbSet&lt;WeatherForecastEntity&gt; WeatherForecasts { get; init; }    public ApplicationDbContext(DbContextOptions&lt;ApplicationDbContext&gt; options)    : base(options)    {    }    protected override void OnModelCreating(ModelBuilder modelBuilder)    {        modelBuilder.Entity&lt;WeatherForecastEntity&gt;().ComplexProperty(e =&gt; e.Data);    }}public class WeatherForecastEntity{    public long Id { get; init; }    public required WeatherForecast Data { get; init; }}public record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary);<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:87px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<\/div>\n<\/details>\n<p>\u041d\u0430\u0448\u0430 \u0446\u0435\u043b\u044c \u2014 \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0434\u0435\u0442\u0435\u0440\u043c\u0438\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u0442\u0435\u0441\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0432\u0435\u0441\u044c \u0446\u0438\u043a\u043b: \u043e\u0442 HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0434\u043e \u0437\u0430\u043f\u0438\u0441\u0438 \u0432 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<p><a class=\"anchor\" name=\"%D0%BF%D1%80%D0%BE%D0%B1%D0%BB%D0%B5%D0%BC%D1%8B\" id=\"\u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b\"><\/a><\/p>\n<h3>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0441\u043f\u043e\u0441\u043e\u0431\u0430 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f<\/h3>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e:<\/p>\n<ol>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0442\u044c <code>CustomWebApplicationFactory<\/code>, \u0443\u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0432 \u043e\u0442 <code>WebApplicationFactory&lt;Program&gt;<\/code>.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c <code>ConfigureWebHost<\/code>, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c <code>TimeProvider<\/code> \u0438 <code>Random<\/code> \u043d\u0430 \u043c\u043e\u043a\u0438.<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u0443\u044e \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430, \u0438\u043d\u0430\u0447\u0435 \u0442\u0435\u0441\u0442\u044b \u0431\u0443\u0434\u0443\u0442 \u043c\u0435\u0448\u0430\u0442\u044c \u0434\u0440\u0443\u0433 \u0434\u0440\u0443\u0433\u0443.<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0442\u044c <code>HttpClient<\/code> \u0447\u0435\u0440\u0435\u0437 \u0444\u0430\u0431\u0440\u0438\u043a\u0443.<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430 \u0434\u043b\u044f \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0432\u0441\u0435\u0433\u043e \u0432\u044b\u0448\u0435\u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u043d\u043e\u0433\u043e.<\/p>\n<\/li>\n<li>\n<p>\u041d\u0435 \u0437\u0430\u0431\u044b\u0442\u044c \u043f\u0440\u043e <code>IAsyncDisposable<\/code> \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0431\u0430\u0437\u044b \u0438 \u043e\u0447\u0438\u0441\u0442\u043a\u0438 \u0434\u0440\u0443\u0433\u0438\u0445 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u043f\u043e\u0441\u043b\u0435 \u0442\u0435\u0441\u0442\u0430.<\/p>\n<\/li>\n<\/ol>\n<p>\u0418 \u044d\u0442\u043e \u2014 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f <strong>\u043e\u0434\u043d\u043e\u0433\u043e<\/strong> \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430. \u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u044c\u0442\u0435, \u0447\u0442\u043e \u0443 \u0432\u0430\u0441 \u0438\u0445 \u0434\u0435\u0441\u044f\u0442\u043e\u043a, \u0438 \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c <code>WebApplicationFactory<\/code>. \u041f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0438\u0435\u0440\u0430\u0440\u0445\u0438\u044f \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0445 \u043a\u043b\u0430\u0441\u0441\u043e\u0432, \u043a\u043e\u0434 \u043a\u043e\u043f\u0438\u0440\u0443\u0435\u0442\u0441\u044f. \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c \u0438 \u0447\u0438\u0442\u0430\u0442\u044c \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0432\u0441\u0451 \u0441\u043b\u043e\u0436\u043d\u0435\u0435. <strong>\u041e\u0442\u0434\u0435\u043b\u044c\u043d\u0430\u044f \u0431\u043e\u043b\u044c \u2014 \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0439 \u043a\u043e\u0434 \u0432 \u0434\u0440\u0443\u0433\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445.<\/strong><\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434 \u0442\u0435\u0441\u0442\u043e\u0432, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435 \u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0430<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cs\">using System.Data.Common;using System.Net;using AwesomeAssertions;using AwesomeAssertions.Json;using FEFF.TestFixtures.AspNetCore.Randomness;using Microsoft.AspNetCore.Hosting;using Microsoft.AspNetCore.Mvc.Testing;using Microsoft.EntityFrameworkCore;using Microsoft.Extensions.Configuration;using Microsoft.Extensions.DependencyInjection;using Microsoft.Extensions.DependencyInjection.Extensions;using Microsoft.Extensions.Time.Testing;using Newtonsoft.Json.Linq;using WebApiTestSubject;namespace ExampleTests.AspNetCore.Basic;internal class CustomWebApplicationFactory : WebApplicationFactory&lt;Program&gt;{    public FakeTimeProvider FakeTime { get; } = new();    public FakeRandom FakeRandom { get; } = new();    protected override void ConfigureWebHost(IWebHostBuilder builder)    {        base.ConfigureWebHost(builder);                builder.ConfigureServices((ctx, _) =&gt;        {            ctx.Configuration.AddSuffixToConnectionString(\"PgDb\", Guid.NewGuid().ToString());        });        builder.ConfigureServices(services =&gt;            services.TryReplaceSingleton&lt;TimeProvider&gt;(FakeTime)        );                builder.ConfigureServices(services =&gt;            services.TryReplaceSingleton&lt;Random&gt;(FakeRandom)        );        builder.UseSetting(\"summary\", \"sunny\");    }}public sealed class BasicApiTests : IAsyncLifetime{    internal CustomWebApplicationFactory App { get; }    internal HttpClient Client { get; }    internal AsyncServiceScope Scope { get; }    internal FakeTimeProvider AppTime =&gt; App.FakeTime;    internal FakeRandom AppRandom =&gt; App.FakeRandom;    internal ApplicationDbContext AppDbCtx =&gt; Scope.ServiceProvider.GetRequiredService&lt;ApplicationDbContext&gt;();    public BasicApiTests()    {        App = new();        Client = App.CreateClient();        \/\/ Application starts here        Scope = App.Services.CreateAsyncScope();    }    public async ValueTask DisposeAsync()    {        await AppDbCtx.Database.EnsureDeletedAsync(TestContext.Current.CancellationToken);        await Scope.DisposeAsync();        Client.Dispose();        await App.DisposeAsync();    }    public async ValueTask InitializeAsync()    {        await AppDbCtx.Database.EnsureCreatedAsync(TestContext.Current.CancellationToken);    }        #region tutorial: ASP.NET Core Application Testing    \/\/\/ &lt;summary&gt;    \/\/\/ Test: POST \/weatherforecast\/generate creates a forecast using time, random, and env var,    \/\/\/ persists it to the database, and GET \/weatherforecast\/today returns it.    \/\/\/    \/\/\/ This test verifies the full integration flow:    \/\/\/ 1. Configure fake time, fake random, and environment variable    \/\/\/ 2. POST to \/weatherforecast\/generate    \/\/\/ 3. Query the database directly to verify persistence    \/\/\/ 4. GET \/weatherforecast\/today to verify the API returns the persisted record    \/\/\/ &lt;\/summary&gt;    [Fact]    public async Task Example_Tutorial_Asp__Api__should_persist_and_return()    {        \/\/ Arrange        var expectedDate = \"2025-06-15\";        var expectedTemperature = 42;        var expectedSummary = \"sunny\";        AppTime.SetUtcNow(DateTimeOffset.Parse($\"{expectedDate}T12:00:00Z\"));        AppRandom.Int32Next = FixedNextStrategy.From(expectedTemperature);        \/\/ ACT        await PostAsync(Client, \"\/weatherforecast\/generate\", null);        \/\/ Assert        var forecastEntities = await AppDbCtx.WeatherForecasts.ToListAsync(TestContext.Current.CancellationToken);        var forecasts = forecastEntities.Select(x =&gt; x.Data).ToList();        JToken.FromObject(forecasts)            .Should().BeEquivalentTo($$\"\"\"            [                {                    \"Date\": \"{{expectedDate}}\",                    \"TemperatureC\": {{expectedTemperature}},                    \"Summary\": \"{{expectedSummary}}\",                }            ]            \"\"\");        \/\/ ACT        var response = await GetAsync(Client, \"\/weatherforecast\/today\");        \/\/ Assert        response            .Should().BeEquivalentTo(            $$\"\"\"            {                \"date\": \"{{expectedDate}}\",                \"temperatureC\": {{expectedTemperature}},                \"summary\": \"{{expectedSummary}}\"            }            \"\"\");    }    # endregion    #region helpers    private static async Task&lt;JToken&gt; GetAsync(HttpClient client, string url)    {        var getResp = await client.GetAsync(url, TestContext.Current.CancellationToken);        var getBody = await getResp.Content.ReadAsStringAsync(TestContext.Current.CancellationToken);        getResp.StatusCode.Should().Be(HttpStatusCode.OK, getBody);        return JToken.Parse(getBody);    }    private static async Task PostAsync(HttpClient client, string url, string? data)    {        StringContent? sc = null;        if (data != null)            sc = new StringContent(data, System.Text.Encoding.UTF8, \"application\/json\");                    var resp = await client.PostAsync(url, sc, TestContext.Current.CancellationToken);        var body = await resp.Content.ReadAsStringAsync(TestContext.Current.CancellationToken);        resp.StatusCode.Should().Be(HttpStatusCode.OK, body);    }    #endregion}internal static class HelperExtensions{    public static IServiceCollection TryReplaceSingleton&lt;TService&gt;(this IServiceCollection services, TService instance)        where TService : class    {        var srcType = typeof(TService);        var oldD = services.SingleOrDefault(d =&gt; d.ServiceType == srcType);        if (oldD == null)            return services;        if(oldD.Lifetime != ServiceLifetime.Singleton)            throw new InvalidOperationException();        var sdNew = new ServiceDescriptor(srcType, instance);        services.Replace(sdNew);        return services;    }        internal static void AddSuffixToConnectionString(this IConfiguration config, string connectionStringName, string suffix)    {        var key = \"ConnectionStrings:\" + connectionStringName;        var cs = config[key];        var csb = new DbConnectionStringBuilder        {            ConnectionString = cs        };        csb[\"Database\"] = $\"{csb[\"Database\"]}-{suffix}\";        var newCs = csb.ConnectionString;        config[key] = newCs;    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<\/div>\n<\/details>\n<p><a class=\"anchor\" name=\"%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5\" id=\"\u0440\u0435\u0448\u0435\u043d\u0438\u0435\"><\/a><\/p>\n<h3>\u0420\u0435\u0448\u0435\u043d\u0438\u0435: \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u044b \u043a\u0430\u043a \u0441\u043f\u043e\u0441\u043e\u0431 \u043c\u044b\u0448\u043b\u0435\u043d\u0438\u044f<\/h3>\n<p>\u042f \u0434\u043e\u043b\u0433\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u044e \u043d\u0430 Python \u0438 C# \u043f\u043e\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e. \u0412 \u044d\u043a\u043e\u0441\u0438\u0441\u0442\u0435\u043c\u0435 Python \u0435\u0441\u0442\u044c \u0442\u0430\u043a\u0430\u044f \u0448\u0442\u0443\u043a\u0430 <a href=\"https:\/\/docs.pytest.org\/en\/stable\/explanation\/fixtures.html\" rel=\"noopener noreferrer nofollow\"><code>@pytest.fixture<\/code><\/a>:<\/p>\n<blockquote>\n<p>In testing, a fixture provides a defined, reliable and consistent context for the tests. This could include environment (for example a database configured with known parameters) or content (such as a dataset).<\/p>\n<\/blockquote>\n<p>\u0418\u043d\u044b\u043c\u0438 \u0441\u043b\u043e\u0432\u0430\u043c\u0438, \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u0430 \u2014 \u044d\u0442\u043e \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0439 (\u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0439) \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u043a\u043e\u0434\u0430, \u0432\u043a\u043b\u044e\u0447\u0430\u044e\u0449\u0438\u0439 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435, \u043c\u0435\u0442\u043e\u0434\u044b \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u043e\u0447\u0438\u0441\u0442\u043a\u0438. \u0421\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u044b \u044f\u0432\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0442\u0435\u0441\u0442\u043e\u0432, \u0432 \u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043a\u0430\u043a \u043c\u0435\u0442\u043e\u0434\u044b \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u043e\u0447\u0438\u0441\u0442\u043a\u0438 \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u043d\u0435\u044f\u0432\u043d\u043e \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u043c \u0434\u0432\u0438\u0436\u043a\u043e\u043c.<\/p>\n<p>\u041d\u0443\u0436\u043d\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044f? <code>pytest.tmp_path<\/code>. \u041d\u0443\u0436\u043d\u043e \u043f\u043e\u0434\u043c\u0435\u043d\u0438\u0442\u044c \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0432\u0440\u0435\u043c\u044f? \u0415\u0441\u0442\u044c <code>pytest-freezegun<\/code>, \u0438 \u0432\u0441\u0451 \u044d\u0442\u043e \u043a\u043e\u043c\u043f\u043e\u043d\u0443\u0435\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u0434\u0435\u043a\u043b\u0430\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 (\u0442\u0430\u043c \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u0430 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u043a\u0430\u043a \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438).<\/p>\n<p>\u041a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437, \u043d\u0430\u0447\u0438\u043d\u0430\u044f \u043d\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u043d\u0430 C#, \u044f \u043b\u043e\u0432\u043b\u044e \u0441\u0435\u0431\u044f \u043d\u0430 \u043c\u044b\u0441\u043b\u0438: \u00ab\u0416\u0430\u043b\u044c, \u0447\u0442\u043e \u043d\u0435\u0442 \u0444\u0438\u043a\u0441\u0442\u0443\u0440 \u043a\u0430\u043a \u0432 Python. \u041f\u043e\u0447\u0435\u043c\u0443 \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430 \u043c\u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c (\u0441\u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438 \u043f\u043e\u043f\u0440\u0430\u0432\u0438\u0442\u044c) \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043a\u043b\u0430\u0441\u0441\u043e\u0432?\u00bb \u0422\u0430\u043a \u0440\u043e\u0434\u0438\u043b\u0430\u0441\u044c \u0438\u0434\u0435\u044f \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u044c \u0444\u0438\u043b\u043e\u0441\u043e\u0444\u0438\u044e pytest-\u0444\u0438\u043a\u0441\u0442\u0443\u0440 \u0432 .NET. \u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u2014 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 <strong>FEFF.TestFixtures<\/strong>.<\/p>\n<p>\u042d\u0442\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0442\u0430\u043a:<\/p>\n<ul>\n<li>\n<p>\u0412\u044b \u043f\u043e\u043c\u0435\u0447\u0430\u0435\u0442\u0435 \u043a\u043b\u0430\u0441\u0441 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u043c <code>[Fixture]<\/code>.<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u0430 \u043d\u0443\u0436\u0434\u0430\u0435\u0442\u0441\u044f \u0432 \u0434\u0440\u0443\u0433\u0438\u0445 \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u0430\u0445, \u0432\u044b \u043e\u0431\u044a\u044f\u0432\u043b\u044f\u0435\u0442\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0447\u0435\u0440\u0435\u0437 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u0430. \u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u0442 \u0433\u0440\u0430\u0444 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439.<\/p>\n<\/li>\n<li>\n<p>\u0412\u044b \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u0442\u0435 \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u0443 \u0432 \u0442\u0435\u0441\u0442\u0435 \u0447\u0435\u0440\u0435\u0437 <code>TestContext.Current.GetFeffFixture&lt;T&gt;()<\/code>.<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043e\u0441\u0432\u043e\u0431\u043e\u0434\u0438\u0442\u044c \u0440\u0435\u0441\u0443\u0440\u0441\u044b, \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c <code>IDisposable<\/code> \u0438\u043b\u0438 <code>IAsyncDisposable<\/code>.<\/p>\n<\/li>\n<li>\n<p>Scope (\u043e\u0431\u043b\u0430\u0441\u0442\u044c \u0432\u0438\u0434\u0438\u043c\u043e\u0441\u0442\u0438) \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442, \u043a\u043e\u0433\u0434\u0430 \u0441\u043e\u0437\u0434\u0430\u0451\u0442\u0441\u044f \u0438 \u0443\u0434\u0430\u043b\u044f\u0435\u0442\u0441\u044f \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u0430: \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u0442\u0435\u0441\u0442, \u043d\u0430 \u043a\u043b\u0430\u0441\u0441, \u043d\u0430 \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u044e \u0438\u043b\u0438 \u043d\u0430 \u0432\u0441\u044e \u0441\u0431\u043e\u0440\u043a\u0443.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043a\u043e\u043c\u0431\u0438\u043d\u0438\u0440\u0443\u0439\u0442\u0435 \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u044b \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u043c\u043f\u043e\u0437\u0438\u0446\u0438\u044e.<\/p>\n<\/li>\n<li>\n<p>\u0414\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f <code>ASP.NET Core Web API<\/code> \u0435\u0441\u0442\u044c \u043d\u0430\u0431\u043e\u0440 \u0433\u043e\u0442\u043e\u0432\u044b\u0445 \u0444\u0438\u043a\u0441\u0442\u0443\u0440. \u041f\u0440\u0438\u0432\u0435\u0442, \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435!<\/p>\n<\/li>\n<\/ul>\n<p>\u0417\u0432\u0443\u0447\u0438\u0442 \u0437\u043d\u0430\u043a\u043e\u043c\u043e? \u0414\u0430, \u044d\u0442\u043e \u0442\u043e\u0442 \u0436\u0435 \u043f\u043e\u0434\u0445\u043e\u0434, \u0447\u0442\u043e \u0438 \u0432 pytest. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435.<\/p>\n<p><a class=\"anchor\" name=\"%D0%BF%D1%80%D0%B0%D0%BA%D1%82%D0%B8%D0%BA%D0%B0\" id=\"\u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0430\"><\/a><\/p>\n<h3>\u041f\u0440\u0430\u043a\u0442\u0438\u043a\u0430: \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c WeatherForecast API<\/h3>\n<h4>\u0428\u0430\u0433 1. \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u043f\u0430\u043a\u0435\u0442\u044b \u043a \u043f\u0440\u043e\u0435\u043a\u0442\u0443 \u0441 \u0442\u0435\u0441\u0442\u0430\u043c\u0438 (xUnit.v3)<\/h4>\n<p>\u0412 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a <code>xUnit<\/code> \u0432\u0435\u0440\u0441\u0438\u0438 3 \u0438 \u0432\u044b\u0448\u0435.<br \/>\u0422\u0430\u043a\u0436\u0435 \u0435\u0441\u0442\u044c \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 <code>TUnit<\/code>.<\/p>\n<pre><code class=\"bash\">dotnet add package FEFF.TestFixtures.XunitV3dotnet add package FEFF.TestFixtures.AspNetCoredotnet add package FEFF.TestFixtures.AspNetCore.EFdotnet add package AwesomeAssertionsdotnet add package AwesomeAssertions.Json<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<blockquote>\n<p><code>AwesomeAssertions<\/code> \u2014 \u0444\u043e\u0440\u043a <code>FluentAssertions<\/code>, \u0434\u0435\u043b\u0430\u0435\u0442 \u0430\u0441\u0441\u0435\u0440\u0442\u044b \u0431\u043e\u043b\u0435\u0435 \u0443\u0434\u043e\u0431\u043d\u044b\u043c\u0438.<\/p>\n<\/blockquote>\n<h4>\u0428\u0430\u0433 2. \u0410\u043a\u0442\u0438\u0432\u0438\u0440\u0443\u0435\u043c \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435<\/h4>\n<p>\u0412 \u043b\u044e\u0431\u043e\u043c \u0444\u0430\u0439\u043b\u0435 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0430\u0442\u0440\u0438\u0431\u0443\u0442 \u0434\u043b\u044f \u0430\u043a\u0442\u0438\u0432\u0430\u0446\u0438\u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0444\u0438\u043a\u0441\u0442\u0443\u0440:<\/p>\n<pre><code class=\"cs\">[assembly: FEFF.TestFixtures.Xunit.TestFixturesExtension]<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h4>\u0428\u0430\u0433 3. \u0418\u0437\u043e\u043b\u0438\u0440\u0443\u0435\u043c \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445<\/h4>\n<p>\u0427\u0442\u043e\u0431\u044b \u0442\u0435\u0441\u0442\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u043b\u0438\u0441\u044c \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e \u0438 \u043d\u0435 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u043e\u0432\u0430\u043b\u0438 \u0434\u0440\u0443\u0433 \u0441 \u0434\u0440\u0443\u0433\u043e\u043c, \u043a\u0430\u0436\u0434\u044b\u0439 \u0442\u0435\u0441\u0442 \u0434\u043e\u043b\u0436\u0435\u043d \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441\u043e \u0441\u0432\u043e\u0435\u0439 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u0443\u044e \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u0443:<\/p>\n<pre><code class=\"cs\">[Fixture]public class OptionsFixture : ITmpDatabaseNameFixtureOptions{    public IReadOnlyCollection&lt;string&gt; ConnectionStringNames =&gt; [\"PgDb\"];}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u042d\u0442\u043e\u0442 \u043a\u043b\u0430\u0441\u0441 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0434\u043b\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 <code>TmpDatabaseNameFixture<\/code>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u0438\u0442 connection string <code>\"PgDb\"<\/code> \u0438 \u043f\u043e\u0434\u043c\u0435\u043d\u0438\u0442 \u043f\u043e\u043b\u0435 <code>Database<\/code>, \u0434\u043e\u0431\u0430\u0432\u0438\u0432 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0441\u0443\u0444\u0444\u0438\u043a\u0441.<\/p>\n<h4>\u0428\u0430\u0433 4. \u041a\u043e\u043c\u043f\u043e\u043d\u0443\u0435\u043c FixtureSet<\/h4>\n<p>\u0412\u043c\u0435\u0441\u0442\u043e \u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u044f \u043e\u0442 \u0431\u0430\u0437\u043e\u0432\u044b\u0445 \u043a\u043b\u0430\u0441\u0441\u043e\u0432 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043a\u043e\u043c\u043f\u043e\u0437\u0438\u0446\u0438\u044e. \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c <code>FixtureSet<\/code> \u2014 record, \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u044e\u0449\u0438\u0439 \u0432\u0441\u0451 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0435 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0430\u0448\u0435\u0433\u043e API:<\/p>\n<pre><code class=\"cs\">[Fixture]public record FixtureSet(    AppManagerFixture&lt;Program&gt; AppManagerFx,    FakeRandomFixture&lt;Program&gt; FakeRandomFx,    FakeTimeFixture&lt;Program&gt; FakeTimeFx,    AppClientFixture&lt;Program&gt; ClientFx,    DatabaseLifecycleFixture&lt;Program, ApplicationDbContext&gt; DbFx,    TmpDatabaseNameFixture&lt;Program, OptionsFixture&gt; TmpDbNameFx);<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041a\u0430\u0436\u0434\u044b\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u2014 \u044d\u0442\u043e \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u0430, \u0440\u0435\u0448\u0430\u044e\u0449\u0430\u044f \u0441\u0432\u043e\u044e \u0437\u0430\u0434\u0430\u0447\u0443:<\/p>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<td data-colwidth=\"145\" width=\"145\">\n<p align=\"left\"><strong>\u0424\u0438\u043a\u0441\u0442\u0443\u0440\u0430<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>AppManagerFixture<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"145\" width=\"145\">\n<p align=\"left\"><strong>\u0427\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0423\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0436\u0438\u0437\u043d\u0435\u043d\u043d\u044b\u043c \u0446\u0438\u043a\u043b\u043e\u043c \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043c\u0435\u043d\u044f\u0442\u044c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e <strong>\u0434\u043e<\/strong> \u0437\u0430\u043f\u0443\u0441\u043a\u0430.<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"145\" width=\"145\">\n<p align=\"left\"><strong>\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>Program<\/code> \u2014 \u0442\u043e\u0447\u043a\u0430 \u0432\u0445\u043e\u0434\u0430 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f (\u0432\u043d\u0443\u0442\u0440\u0438 \u044d\u0442\u043e\u0439 \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u044b \u0441\u043e\u0437\u0434\u0430\u0451\u0442\u0441\u044f <code>WebApplicationFactory&lt;Program&gt;<\/code>)<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<td data-colwidth=\"145\" width=\"145\">\n<p align=\"left\"><strong>\u0424\u0438\u043a\u0441\u0442\u0443\u0440\u0430<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>AppClientFixture<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"145\" width=\"145\">\n<p align=\"left\"><strong>\u0427\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 <code>HttpClient<\/code>, \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0451\u043d\u043d\u044b\u0439 \u043a \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u043c\u0443 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044e.<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"145\" width=\"145\">\n<p align=\"left\"><strong>\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>Program<\/code> \u2014 *<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<td data-colwidth=\"145\" width=\"145\">\n<p align=\"left\"><strong>\u0424\u0438\u043a\u0441\u0442\u0443\u0440\u0430<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>FakeRandomFixture<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"145\" width=\"145\">\n<p align=\"left\"><strong>\u0427\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041f\u043e\u0434\u043c\u0435\u043d\u044f\u0435\u0442 <code>Random<\/code> \u043d\u0430 \u0434\u0435\u0442\u0435\u0440\u043c\u0438\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440.<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"145\" width=\"145\">\n<p align=\"left\"><strong>\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>Program<\/code> \u2014 *<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<td data-colwidth=\"145\" width=\"145\">\n<p align=\"left\"><strong>\u0424\u0438\u043a\u0441\u0442\u0443\u0440\u0430<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>FakeTimeFixture<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"145\" width=\"145\">\n<p align=\"left\"><strong>\u0427\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041f\u043e\u0434\u043c\u0435\u043d\u044f\u0435\u0442 <code>TimeProvider<\/code>. \u041c\u043e\u0436\u043d\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u043b\u044e\u0431\u0443\u044e \u0434\u0430\u0442\u0443.<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"145\" width=\"145\">\n<p align=\"left\"><strong>\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>Program<\/code> \u2014 *<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<td data-colwidth=\"145\" width=\"145\">\n<p align=\"left\"><strong>\u0424\u0438\u043a\u0441\u0442\u0443\u0440\u0430<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>DatabaseLifecycleFixture<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"145\" width=\"145\">\n<p align=\"left\"><strong>\u0427\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0421\u043e\u0437\u0434\u0430\u0451\u0442, \u0443\u0434\u0430\u043b\u044f\u0435\u0442 \u0438 \u0434\u0430\u0451\u0442 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0435 \u0442\u0435\u0441\u0442\u0430.<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"145\" width=\"145\">\n<p align=\"left\"><strong>\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>Program<\/code> \u2014 *<br \/><code>ApplicationDbContext<\/code> \u2014 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 EF Core<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<td data-colwidth=\"145\" width=\"145\">\n<p align=\"left\"><strong>\u0424\u0438\u043a\u0441\u0442\u0443\u0440\u0430<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>TmpDatabaseNameFixture<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"145\" width=\"145\">\n<p align=\"left\"><strong>\u0427\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u0442 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0435 \u0438\u043c\u044f \u0411\u0414 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430.<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"145\" width=\"145\">\n<p align=\"left\"><strong>\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>Program<\/code> \u2014 *<br \/><code>OptionsFixture<\/code> \u2014 \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u0430 \u0441 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0435\u0439 (\u0441 \u0435\u0451 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442\u0441\u044f \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043d\u0430\u0434\u043e \u043f\u0440\u043e\u043f\u0430\u0442\u0447\u0438\u0442\u044c)<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>\u041f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u0435: \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u044b \u0441 \u043f\u043e\u043c\u0435\u0442\u043a\u043e\u0439 (*) \u0437\u0430\u0432\u0438\u0441\u044f\u0442 \u043e\u0442 <code>AppManagerFixture&lt;Program&gt;<\/code>, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0438\u043c \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u0434\u043c\u0435\u043d\u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u0432 DI-\u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u0427\u0442\u043e\u0431\u044b \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0444\u0438\u043a\u0441\u0442\u0443\u0440 \u0441\u043c\u043e\u0433\u043b\u0430 \u0440\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438, \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 <code>Program<\/code>. \u0412\u0441\u0451 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0435 \u0431\u0443\u0434\u0435\u0442 \u0441\u0434\u0435\u043b\u0430\u043d\u043e \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438.<\/p>\n<h4>\u0428\u0430\u0433 5. \u0421\u043e\u0437\u0434\u0430\u0451\u043c \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u043a\u043b\u0430\u0441\u0441<\/h4>\n<pre><code class=\"cs\">public class ApiTests{    \/\/ \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u044b \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0443\u044e\u0442\u0441\u044f \u0442\u0443\u0442    protected FixtureSet FixtureSet { get; } =        TestContext.Current.GetFeffFixture&lt;FixtureSet&gt;();    \/\/ \u0423\u0434\u043e\u0431\u043d\u044b\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u0434\u043b\u044f \u0431\u044b\u0441\u0442\u0440\u043e\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430    protected FakeRandom AppRandom =&gt;         FixtureSet.FakeRandomFx.Value;    protected FakeTimeProvider AppTime =&gt;         FixtureSet.FakeTimeFx.Value;    protected IAppConfigurator AppConfigurationBuilder =&gt;         FixtureSet.AppManagerFx.ConfigurationBuilder;    protected HttpClient Client =&gt;         FixtureSet.ClientFx.LazyValue;    protected ApplicationDbContext AppDbCtx =&gt;         FixtureSet.DbFx.LazyDbContext;    protected IDatabaseLifecycleFixture DbFx =&gt;         FixtureSet.DbFx;}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412\u0441\u0451. \u0422\u0435\u0441\u0442\u043e\u0432\u0430\u044f \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0433\u043e\u0442\u043e\u0432\u0430!<\/p>\n<h4>\u0428\u0430\u0433 6. \u041f\u0438\u0448\u0435\u043c \u0442\u0435\u0441\u0442\u044b<\/h4>\n<pre><code class=\"cs\">    [Fact]    public async Task Generate_weatherforecast__should_persist_and_return()    {        \/\/ Arrange        var expectedDate = \"2025-06-15\";        var expectedTemperature = 42;        var expectedSummary = \"sunny\";        \/\/ \u0424\u0438\u043a\u0441\u0438\u0440\u0443\u0435\u043c \u0432\u0440\u0435\u043c\u044f: \u0441\u0435\u0433\u043e\u0434\u043d\u044f 15 \u0438\u044e\u043d\u044f 2025        AppTime.SetUtcNow(DateTimeOffset.Parse($\"{expectedDate}T12:00:00Z\"));        \/\/ \u0424\u0438\u043a\u0441\u0438\u0440\u0443\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0441\u0442\u044c: \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430 \u0432\u0441\u0435\u0433\u0434\u0430 42        AppRandom.Int32Next = FixedNextStrategy.From(expectedTemperature);        \/\/ \u041f\u043e\u0434\u043c\u0435\u043d\u044f\u0435\u043c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u0434\u043e \u0441\u0442\u0430\u0440\u0442\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f        AppConfigurationBuilder.UseSetting(\"summary\", expectedSummary);        \/\/ \u0421\u043e\u0437\u0434\u0430\u0451\u043c \u0438\u0437\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0443\u044e \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445        \/\/ \u0412 \u044d\u0442\u043e\u0442 \u043c\u043e\u043c\u0435\u043d\u0442 \u0441\u0442\u0430\u0440\u0442\u0443\u0435\u0442 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435        await DbFx.EnsureCreatedAsync(TestContext.Current.CancellationToken);        \/\/ Act: \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u043f\u0440\u043e\u0433\u043d\u043e\u0437        await PostAsync(Client, \"\/weatherforecast\/generate\", null);        \/\/ Assert: \u0434\u0430\u043d\u043d\u044b\u0435 \u0440\u0435\u0430\u043b\u044c\u043d\u043e \u043f\u043e\u043f\u0430\u043b\u0438 \u0432 \u0431\u0430\u0437\u0443?        var forecasts = await AppDbCtx.WeatherForecasts            .Select(x =&gt; x.Data)            .ToListAsync(TestContext.Current.CancellationToken);        JToken.FromObject(forecasts)            .Should().BeEquivalentTo($$\"\"\"            [                {                    \"Date\": \"{{expectedDate}}\",                    \"TemperatureC\": {{expectedTemperature}},                    \"Summary\": \"{{expectedSummary}}\"                }            ]            \"\"\");        \/\/ Act: \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u043c \u0447\u0435\u0440\u0435\u0437 API \u043f\u0440\u043e\u0433\u043d\u043e\u0437 \u043d\u0430 \u0441\u0435\u0433\u043e\u0434\u043d\u044f        var response = await GetAsync(Client, \"\/weatherforecast\/today\");        \/\/ Assert: API \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0442\u043e, \u0447\u0442\u043e \u043b\u0435\u0436\u0438\u0442 \u0432 \u0431\u0430\u0437\u0435?        response.Should().BeEquivalentTo($$\"\"\"            {                \"date\": \"{{expectedDate}}\",                \"temperatureC\": {{expectedTemperature}},                \"summary\": \"{{expectedSummary}}\"            }            \"\"\");    }<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h4>\u0427\u0442\u043e \u0436\u0435 \u0437\u0434\u0435\u0441\u044c \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u043e?<\/h4>\n<ol>\n<li>\n<p><strong>\u0414\u0435\u0442\u0435\u0440\u043c\u0438\u043d\u0438\u0437\u043c.<\/strong> \u041c\u044b \u0437\u0430\u0444\u0438\u043a\u0441\u0438\u0440\u043e\u0432\u0430\u043b\u0438 \u0432\u0440\u0435\u043c\u044f \u043d\u0430 15 \u0438\u044e\u043d\u044f 2025 \u0433\u043e\u0434\u0430 \u0438 \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0443 \u043d\u0430 42 \u0433\u0440\u0430\u0434\u0443\u0441\u0430. \u0422\u0435\u0441\u0442 \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u0445 \u0447\u0430\u0441\u043e\u0432 \u0438 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0441\u0442\u0438. \u0424\u0438\u043a\u0441\u0442\u0443\u0440\u044b \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e \u043d\u0430\u0448\u043b\u0438 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438 \u043e\u0431\u043d\u043e\u0432\u0438\u043b\u0438 \u0435\u0433\u043e DI-\u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440.<\/p>\n<\/li>\n<li>\n<p><strong>\u0413\u0438\u0431\u043a\u043e\u0441\u0442\u044c.<\/strong> \u041c\u044b \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u0438 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0432 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u201csunny\u201d \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 \u201csummary\u201d. \u041c\u044b \u043c\u043e\u0436\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0442\u043e \u043c\u043e\u0434\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430.<\/p>\n<\/li>\n<li>\n<p><strong>\u0418\u0437\u043e\u043b\u044f\u0446\u0438\u044f.<\/strong> <code>TmpDatabaseNameFixture<\/code> \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u0442, \u0447\u0442\u043e \u044d\u0442\u043e\u0442 \u0442\u0435\u0441\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0441\u043e \u0441\u0432\u043e\u0435\u0439 \u0431\u0430\u0437\u043e\u0439. \u0414\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0441\u043e\u0442\u043d\u044e \u0442\u0435\u0441\u0442\u043e\u0432 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e \u2014 \u043a\u0430\u0436\u0434\u044b\u0439 \u043f\u043e\u043b\u0443\u0447\u0438\u0442 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u0443\u044e \u0411\u0414.<\/p>\n<\/li>\n<li>\n<p><strong>\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u043e\u0447\u0438\u0441\u0442\u043a\u0430.<\/strong> \u041f\u043e\u0441\u043b\u0435 \u0442\u0435\u0441\u0442\u0430 <code>DatabaseLifecycleFixture<\/code> \u0443\u0434\u0430\u043b\u0438\u0442 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e \u0431\u0430\u0437\u0443, <code>AppClientFixture<\/code> \u043e\u0441\u0432\u043e\u0431\u043e\u0434\u0438\u0442 <code>HttpClient<\/code>, \u0430 <code>AppManagerFixture<\/code> \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435. \u041c\u044b \u043d\u0435 \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0438 \u043d\u0438 \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u0447\u043a\u0438 \u043a\u043e\u0434\u0430 \u043e\u0447\u0438\u0441\u0442\u043a\u0438.<\/p>\n<\/li>\n<\/ol>\n<p><a class=\"anchor\" name=\"%D1%81%D1%80%D0%B0%D0%B2%D0%BD%D0%B5%D0%BD%D0%B8%D0%B5\" id=\"\u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0435\"><\/a><\/p>\n<h3>\u0421\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0435 \u0441 \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430\u043c\u0438<\/h3>\n<h4>xUnit Native Fixtures<\/h4>\n<p>\u041d\u0430\u0442\u0438\u0432\u043d\u044b\u0439 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c xUnit (<code>IClassFixture&lt;T&gt;<\/code>, <code>ICollectionFixture&lt;T&gt;<\/code>, <code>AssemblyFixtureAttribute<\/code>) \u0440\u0435\u0448\u0430\u0435\u0442 \u0442\u0443 \u0436\u0435 \u0437\u0430\u0434\u0430\u0447\u0443, \u043d\u043e \u0441 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u043c\u0438 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f\u043c\u0438:<\/p>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th data-colwidth=\"246\" width=\"246\">\n<p align=\"left\">\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c<\/p>\n<\/th>\n<th>\n<p align=\"left\">FEFF.TestFixtures<\/p>\n<\/th>\n<th>\n<p align=\"left\">xUnit Native<\/p>\n<\/th>\n<\/tr>\n<tr>\n<td data-colwidth=\"246\" width=\"246\">\n<p align=\"left\">\u0424\u0438\u043a\u0441\u0442\u0443\u0440\u0430 \u0441 \u043e\u0431\u043b\u0430\u0441\u0442\u044c\u044e \u0432\u0438\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u2705 \u0415\u0441\u0442\u044c<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u274c \u041d\u0435\u0442<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"246\" width=\"246\">\n<p align=\"left\">\u0420\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u043c\u0435\u0436\u0434\u0443 \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u0430\u043c\u0438<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u2705 \u0415\u0441\u0442\u044c<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u274c \u041d\u0435\u0442<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"246\" width=\"246\">\n<p align=\"left\">\u0423\u0434\u043e\u0431\u0441\u0442\u0432\u043e \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0444\u0438\u043a\u0441\u0442\u0443\u0440<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u2705 \u041e\u0434\u0438\u043d \u043c\u0435\u0442\u043e\u0434<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u26a0\ufe0f \u041d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u0432 \u0438 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"246\" width=\"246\">\n<p align=\"left\">\u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0435 \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u044b<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u2705 \u0414\u043b\u044f ASP.NET Core, \u0411\u0414, \u0432\u0440\u0435\u043c\u0435\u043d\u0438<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u274c \u041d\u0435\u0442<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"246\" width=\"246\">\n<p align=\"left\">Async setup<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u26a0\ufe0f \u0420\u0443\u0447\u043d\u043e\u0439 \u0432\u044b\u0437\u043e\u0432<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u2705 <code>IAsyncLifetime<\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>\u0413\u043b\u0430\u0432\u043d\u043e\u0435 \u043e\u0442\u043b\u0438\u0447\u0438\u0435: \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f <code>xUnit<\/code>, \u043f\u0440\u0438\u0434\u0451\u0442\u0441\u044f \u043b\u0438\u0431\u043e \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c setup-\u043a\u043e\u0434, \u043b\u0438\u0431\u043e \u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0441\u043b\u043e\u0436\u043d\u044b\u0435 \u0438\u0435\u0440\u0430\u0440\u0445\u0438\u0438 \u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u044f. <code>FEFF.TestFixtures<\/code> \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0435\u0442 \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u043c\u043f\u043e\u0437\u0438\u0446\u0438\u044e.<\/p>\n<h4>Pytest<\/h4>\n<p>\u0414\u043b\u044f \u0442\u0435\u0445, \u043a\u0442\u043e \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442 \u0438\u0437 Python:<\/p>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th>\n<p align=\"left\">\n<\/th>\n<th>\n<p align=\"left\">Python (pytest)<\/p>\n<\/th>\n<th>\n<p align=\"left\">.NET (FEFF.TestFixtures)<\/p>\n<\/th>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">\u041e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u0438\u0435 \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u044b<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0410\u0442\u0440\u0438\u0431\u0443\u0442 <code>@pytest.fixture<\/code> \u043d\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u044e<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0410\u0442\u0440\u0438\u0431\u0443\u0442 <code>[Fixture]<\/code> \u043d\u0430 \u043a\u043b\u0430\u0441\u0441<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0432 \u0442\u0435\u0441\u0442\u0435<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0410\u0440\u0433\u0443\u043c\u0435\u043d\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u0438: <code>def test_something(db, client):<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0412\u044b\u0437\u043e\u0432 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 <code>TestContext.Current.GetFeffFixture&lt;T&gt;()<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">\u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043e\u0431\u043b\u0430\u0441\u0442\u044c\u044e \u0432\u0438\u0434\u0438\u043c\u043e\u0441\u0442\u0438<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>scope=\"function\"<\/code> \/ <code>\"session\"<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>FixtureScopeType.TestCase<\/code> \/ <code>Assembly<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">\u0424\u0438\u043a\u0441\u0442\u0443\u0440\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 \u043f\u0430\u043f\u043a\u0438<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>tmp_path<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>TmpDirectoryFixture<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">\u0424\u0438\u043a\u0441\u0442\u0443\u0440\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u0438<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>pytest-freezegun<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>FakeTimeFixture<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">\u0424\u0438\u043a\u0441\u0442\u0443\u0440\u0430 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>pytest-postgresql<\/code><\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>TmpDatabaseNameFixture<\/code> + <code>DatabaseLifecycleFixture<\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>\u0424\u0438\u043b\u043e\u0441\u043e\u0444\u0438\u044f \u0442\u0430 \u0436\u0435 \u2014 \u0434\u0435\u043a\u043b\u0430\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0435 \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u0438\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439, \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0436\u0438\u0437\u043d\u0435\u043d\u043d\u044b\u043c \u0446\u0438\u043a\u043b\u043e\u043c, \u043a\u043e\u043c\u043f\u043e\u0437\u0438\u0446\u0438\u044f \u0432\u043c\u0435\u0441\u0442\u043e \u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u044f.<\/p>\n<p>\u041e\u0442\u043b\u0438\u0447\u0438\u044f:<\/p>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th>\n<p align=\"left\">\n<\/th>\n<th>\n<p align=\"left\">Python (pytest)<\/p>\n<\/th>\n<th>\n<p align=\"left\">.NET (FEFF.TestFixtures)<\/p>\n<\/th>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">\u041e\u0431\u043b\u0430\u0441\u0442\u044c \u0432\u0438\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u044b<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f \u0430\u0432\u0442\u043e\u0440\u043e\u043c \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u044b \u043f\u0440\u0438 \u0435\u0451 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f \u0430\u0432\u0442\u043e\u0440\u043e\u043c \u0442\u0435\u0441\u0442\u0430 \u043f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u044b<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">\u0421\u043c\u0435\u0448\u0435\u043d\u0438\u0435 \u043e\u0431\u043b\u0430\u0441\u0442\u0435\u0439 \u0432\u0438\u0434\u0438\u043c\u043e\u0441\u0442\u0438<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0424\u0438\u043a\u0441\u0442\u0443\u0440\u044b \u043c\u043e\u0433\u0443\u0442 \u0437\u0430\u0432\u0438\u0441\u0435\u0442\u044c \u043e\u0442 \u0444\u0438\u043a\u0441\u0442\u0443\u0440 \u0441 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u043e\u0431\u043b\u0430\u0441\u0442\u044f\u043c\u0438 \u0432\u0438\u0434\u0438\u043c\u043e\u0441\u0442\u0438<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0417\u0430\u0432\u0438\u0441\u0438\u043c\u044b\u0435 \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u044b \u0441\u043e\u0437\u0434\u0430\u044e\u0442\u0441\u044f \u0432 \u0442\u043e\u0439 \u0436\u0435 \u043e\u0431\u043b\u0430\u0441\u0442\u0438 \u0432\u0438\u0434\u0438\u043c\u043e\u0441\u0442\u0438<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p><a class=\"anchor\" name=\"%D0%B7%D0%B0%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B5\" id=\"\u0437\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\"><\/a><\/p>\n<h3>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435: \u0447\u0442\u043e \u0441\u0442\u0430\u043b\u043e \u043b\u0443\u0447\u0448\u0435<\/h3>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0434\u0432\u0435\u0434\u0451\u043c \u0438\u0442\u043e\u0433. \u0412 \u043d\u0430\u0447\u0430\u043b\u0435 \u0441\u0442\u0430\u0442\u044c\u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u0442\u0435\u0441\u0442 ASP.NET Core API \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u043b \u0442\u0430\u043a:<\/p>\n<ul>\n<li>\n<p>\u041d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u0435 \u043e\u0442 <code>WebApplicationFactory<\/code>.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 <code>ConfigureWebHost<\/code>.<\/p>\n<\/li>\n<li>\n<p>Setup-\u043a\u043e\u0434 \u0432 \u043a\u043b\u0430\u0441\u0441\u0435 \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<\/li>\n<li>\n<p><code>IDisposable<\/code> \u0432 \u043a\u043b\u0430\u0441\u0441\u0435 \u0442\u0435\u0441\u0442\u043e\u0432 \u0434\u043b\u044f \u043e\u0447\u0438\u0441\u0442\u043a\u0438.<\/p>\n<\/li>\n<li>\n<p>\u0414\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432\u0441\u0435\u0433\u043e \u044d\u0442\u043e\u0433\u043e \u0432 \u043a\u0430\u0436\u0434\u043e\u043c \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u043c \u043a\u043b\u0430\u0441\u0441\u0435 \u0438 <strong>\u0432 \u043a\u0430\u0436\u0434\u043e\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435<\/strong>.<\/p>\n<\/li>\n<\/ul>\n<p>\u0421 FEFF.TestFixtures.AspNetCore:<\/p>\n<ul>\n<li>\n<p>\u2705 \u0414\u0435\u043a\u043b\u0430\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0442\u0435\u0441\u0442\u043e\u0432 \u0447\u0435\u0440\u0435\u0437 <code>FixtureSet<\/code> (\u0437\u0430 \u0441\u0447\u0451\u0442 \u043a\u043e\u043c\u043f\u043e\u0437\u0438\u0446\u0438\u0438).<\/p>\n<\/li>\n<li>\n<p>\u2705 \u0415\u0441\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0434\u043e\u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c <code>WebApplicationFactory<\/code> \u043f\u043e\u0434 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0442\u0435\u0441\u0442 (\u0437\u0430 \u0441\u0447\u0451\u0442 \u043a\u043e\u043c\u043f\u043e\u0437\u0438\u0446\u0438\u0438).<\/p>\n<\/li>\n<li>\n<p>\u2705 Setup-\u043a\u043e\u0434 \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442 \u0440\u043e\u0432\u043d\u043e 1 \u0441\u0442\u0440\u043e\u043a\u0443.<\/p>\n<\/li>\n<li>\n<p>\u2705 \u041d\u0435\u0442 \u043a\u043e\u0434\u0430 \u043e\u0447\u0438\u0441\u0442\u043a\u0438 \u2014 \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u044b \u0431\u0435\u0440\u0443\u0442 \u044d\u0442\u043e \u043d\u0430 \u0441\u0435\u0431\u044f.<\/p>\n<\/li>\n<li>\n<p>\u2705 \u0424\u0438\u043a\u0441\u0442\u0443\u0440\u044b \u043c\u043e\u0436\u043d\u043e (\u0438 \u043d\u0443\u0436\u043d\u043e) \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u0440\u0430\u0437\u043d\u044b\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432.<\/p>\n<\/li>\n<\/ul>\n<p>\u0415\u0441\u043b\u0438 \u0432\u0430\u043c, \u043a\u0430\u043a \u0438 \u043c\u043d\u0435, \u043d\u0435 \u0445\u0432\u0430\u0442\u0430\u0435\u0442 \u044d\u043b\u0435\u0433\u0430\u043d\u0442\u043d\u043e\u0441\u0442\u0438 pytest \u0432 \u043c\u0438\u0440\u0435 .NET \u2014 \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 FEFF.TestFixtures. \u042d\u0442\u043e \u043d\u0435 \u0441\u0435\u0440\u0435\u0431\u0440\u044f\u043d\u0430\u044f \u043f\u0443\u043b\u044f, \u043d\u043e \u044d\u0442\u043e \u0448\u0430\u0433 \u043a \u0442\u043e\u043c\u0443, \u0447\u0442\u043e\u0431\u044b \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u0442\u0430\u043b\u043e \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u0443\u0434\u043e\u0431\u043d\u044b\u043c \u0438 \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u043c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u043c.<\/p>\n<p><a class=\"anchor\" name=\"%D1%81%D1%81%D1%8B%D0%BB%D0%BA%D0%B8\" id=\"\u0441\u0441\u044b\u043b\u043a\u0438\"><\/a><\/p>\n<h3>\u0421\u0441\u044b\u043b\u043a\u0438 \u0438 \u0440\u0435\u0441\u0443\u0440\u0441\u044b<\/h3>\n<ul>\n<li>\n<p>\ud83d\udce6 <strong>NuGet:<\/strong> <a href=\"https:\/\/www.nuget.org\/packages\/FEFF.TestFixtures.AspNetCore\" rel=\"noopener noreferrer nofollow\">FEFF.TestFixtures.AspNetCore<\/a><\/p>\n<\/li>\n<li>\n<p>\ud83d\udcda <strong>\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f:<\/strong> <a href=\"https:\/\/metacoder-feff.github.io\/FEFF.TestFixtures\/\" rel=\"noopener noreferrer nofollow\">https:\/\/metacoder-feff.github.io\/FEFF.TestFixtures\/<\/a><\/p>\n<\/li>\n<li>\n<p>\ud83d\udcbb <strong>\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434:<\/strong> <a href=\"https:\/\/github.com\/metacoder-feff\/FEFF.TestFixtures\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/metacoder-feff\/FEFF.TestFixtures<\/a><\/p>\n<\/li>\n<li>\n<p>\ud83e\uddea <strong>\u041a\u043e\u0434 \u0442\u0435\u0441\u0442\u043e\u0432 \u0438\u0437 \u0441\u0442\u0430\u0442\u044c\u0438:<\/strong> <a href=\"https:\/\/github.com\/metacoder-feff\/FEFF.TestFixtures\/blob\/main\/examples\/ExampleTests.AspNetCore\/ApiTests.cs\" rel=\"noopener noreferrer nofollow\">ApiTests.cs<\/a><\/p>\n<\/li>\n<li>\n<p>\ud83d\udcdd <strong>\u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c:<\/strong> <a href=\"https:\/\/github.com\/metacoder-feff\/FEFF.TestFixtures\/blob\/main\/tests\/Subjects\/WebApiTestSubject\/Program.cs\" rel=\"noopener noreferrer nofollow\">WebApiTestSubject<\/a><\/p>\n<\/li>\n<\/ul>\n<hr\/>\n<p><a class=\"anchor\" name=\"%D0%B1%D0%BE%D0%BD%D1%83%D1%81\" id=\"\u0431\u043e\u043d\u0443\u0441\"><\/a><\/p>\n<h3>\u0411\u043e\u043d\u0443\u0441: \u043c\u0438\u043d\u0443\u0442\u043a\u0430 AI<\/h3>\n<p>\u041f\u043e\u043f\u0440\u043e\u0441\u0438\u0442\u0435 \u0432\u0430\u0448\u0435\u0433\u043e \u0430\u0441\u0441\u0438\u0441\u0442\u0435\u043d\u0442\u0430 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0442\u0430\u043a\u0438\u0435 \u0442\u0435\u0441\u0442\u044b \u0434\u043b\u044f \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043f\u0440\u043e\u043c\u0442\u043e\u043c:<\/p>\n<blockquote>\n<p>In file &lt;\u043f\u0443\u0442\u044c-\u0434\u043e-\u0444\u0430\u0439\u043b\u0430-\u0441-\u0442\u0435\u0441\u0442\u0430\u043c\u0438&gt;.cs<br \/> Create api tests for application &lt;\u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435-\u0438\u043b\u0438-\u043f\u0443\u0442\u044c-\u043f\u0430\u043f\u043a\u0438-\u0441-\u043f\u0440\u043e\u0435\u043a\u0442\u043e\u043c-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c-web-api&gt;<br \/> Using FEFF.TestFixtures Library:<\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/github.com\/metacoder-feff\/FEFF.TestFixtures\/tree\/main\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/metacoder-feff\/FEFF.TestFixtures\/tree\/main<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/metacoder-feff.github.io\/FEFF.TestFixtures\/articles\/tutorials\/asp-net-core-application-testing.html\" rel=\"noopener noreferrer nofollow\">https:\/\/metacoder-feff.github.io\/FEFF.TestFixtures\/articles\/tutorials\/asp-net-core-application-testing.html<\/a><\/p>\n<\/li>\n<\/ul>\n<p>Use latest stable versions of packages FEFF.TestFixtures.*<\/p>\n<\/blockquote>\n<p>\u041d\u0435 \u0437\u0430\u0431\u0443\u0434\u044c\u0442\u0435 \u043f\u043e\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0441\u044e\u0434\u0430 \u043f\u0443\u0442\u0438 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0438 \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u0433\u043e \u0444\u0430\u0439\u043b\u0430 \u0441 \u0442\u0435\u0441\u0442\u0430\u043c\u0438. \u0415\u0441\u043b\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0431\u043e\u043b\u044c\u0448\u043e\u0435, \u0442\u043e \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0438\u0442\u0435 \u0432 \u043f\u0440\u043e\u043c\u0442\u0435 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u044b \u0438\/\u0438\u043b\u0438 \u0431\u0438\u0437\u043d\u0435\u0441-\u0444\u0443\u043d\u043a\u0446\u0438\u0438.<br \/> \u0414\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u043e\u0432 \u2014 \u043a\u0430\u043a \u043e\u0431\u044b\u0447\u043d\u043e \u043f\u0440\u043e\u0441\u0438\u0442\u0435 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u043e \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0438 \u0441 \u0443\u0436\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u043c\u0438.<\/p>\n<hr\/>\n<p><em>\u0410\u0432\u0442\u043e\u0440 \u2014 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u0432\u0435\u0440\u0435\u043d, \u0447\u0442\u043e \u0445\u043e\u0440\u043e\u0448\u0438\u0435 \u0442\u0435\u0441\u0442\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u0438\u0441\u0430\u0442\u044c\u0441\u044f \u0441 \u0442\u0430\u043a\u043e\u0439 \u0436\u0435 \u043b\u0451\u0433\u043a\u043e\u0441\u0442\u044c\u044e, \u043a\u0430\u043a \u0445\u043e\u0440\u043e\u0448\u0438\u0439 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u043d-\u043a\u043e\u0434.<\/em><\/p>\n<p><em>\u0415\u0441\u043b\u0438 \u0441\u0442\u0430\u0442\u044c\u044f \u0431\u044b\u043b\u0430 \u043f\u043e\u043b\u0435\u0437\u043d\u0430 \u2014 \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u0435\u0441\u044c \u043e\u043f\u044b\u0442\u043e\u043c \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445. \u0411\u0443\u0434\u0443 \u0440\u0430\u0434 \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0439 \u0441\u0432\u044f\u0437\u0438 \u0438 \u0432\u043e\u043f\u0440\u043e\u0441\u0430\u043c!<\/em><\/p>\n<\/div>\n<p>\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/1044534\/\">https:\/\/habr.com\/ru\/articles\/1044534\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u0420\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u043c\u044b \u0441\u0442\u0430\u0440\u0430\u0435\u043c\u0441\u044f \u043d\u0435 \u0437\u043b\u043e\u0443\u043f\u043e\u0442\u0440\u0435\u0431\u043b\u044f\u0442\u044c \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043a\u043e\u0434\u0430. \u0418\u0437 \u0447\u0430\u0441\u0442\u043e \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u044e\u0449\u0435\u0433\u043e\u0441\u044f \u043a\u043e\u0434\u0430 \u043c\u044b \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438, \u0430 \u0434\u043b\u044f \u0438\u0445 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0432 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0435 ASP.NET Core \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c DI-\u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440.\u0418\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0434\u043b\u044f ASP.NET Core API, \u043a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u043e, \u0442\u043e\u0436\u0435 \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u0435\u0442\u0441\u044f, \u043d\u043e \u043a\u0430\u043a\u0438\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u043f\u043e\u043c\u043e\u0433\u0430\u044e\u0442 \u043d\u0430\u043c \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u043a\u043e\u0434?\u0420\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 Python \u0440\u0435\u0448\u0430\u044e\u0442 \u044d\u0442\u0443 \u0437\u0430\u0434\u0430\u0447\u0443 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e pytest.fixtures, \u043e\u0434\u043d\u0430\u043a\u043e \u0432 dotnet-\u044d\u043a\u043e\u0441\u0438\u0441\u0442\u0435\u043c\u0435 (xUnit) \u0445\u043e\u0440\u043e\u0448\u0435\u0433\u043e \u0430\u043d\u0430\u043b\u043e\u0433\u0430 \u043f\u043e\u043a\u0430 \u043d\u0435\u0442.\u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043f\u0440\u0438\u043c\u0435\u0440, \u043a\u0430\u043a \u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0440\u043e\u043a \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u043e\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435 \u0441 \u0438\u0437\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0439 \u0411\u0414, \u0444\u0435\u0439\u043a\u043e\u0432\u044b\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c \u0438 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0441\u0442\u044c\u044e, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043a\u0430\u043a \u0434\u043e\u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u044d\u0442\u043e \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u0432\u0437\u044f\u0442\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430.\u0414\u043b\u044f \u043a\u043e\u0433\u043e \u044d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f: \u0434\u043b\u044f \u0431\u044d\u043a\u0435\u043d\u0434-\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 (\u043d\u0430 .NET), \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u043b\u0438\u0434\u043e\u0432, QA-\u0438\u043d\u0436\u0435\u043d\u0435\u0440\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0438\u0448\u0443\u0442 \u043a\u043e\u0434, \u0438 \u0432\u0441\u0435\u0445, \u043a\u0442\u043e \u0443\u0441\u0442\u0430\u043b \u043e\u0442 \u0431\u0435\u0441\u0441\u043c\u044b\u0441\u043b\u0435\u043d\u043d\u043e \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u044e\u0449\u0435\u0433\u043e\u0441\u044f \u043a\u043e\u0434\u0430 \u0432 \u0442\u0435\u0441\u0442\u0430\u0445.\u0415\u0441\u043b\u0438 \u0432\u044b \u043f\u0438\u0448\u0435\u0442\u0435 \u043d\u0430 C#, \u043d\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u0435\u0441\u0442\u0430\u043c \u044d\u043b\u0435\u0433\u0430\u043d\u0442\u043d\u043e\u0441\u0442\u0438 Python \u2014 \u0434\u043e\u0431\u0440\u043e \u043f\u043e\u0436\u0430\u043b\u043e\u0432\u0430\u0442\u044c.\u0421\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435\u0422\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 (WeatherForecast API)\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0441\u043f\u043e\u0441\u043e\u0431\u0430 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f\u0420\u0435\u0448\u0435\u043d\u0438\u0435: \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u044b \u043a\u0430\u043a \u0441\u043f\u043e\u0441\u043e\u0431 \u043c\u044b\u0448\u043b\u0435\u043d\u0438\u044f\u041f\u0440\u0430\u043a\u0442\u0438\u043a\u0430: \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c WeatherForecast API\u0421\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0435 \u0441 \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430\u043c\u0438\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\u0421\u0441\u044b\u043b\u043a\u0438 \u0438 \u0440\u0435\u0441\u0443\u0440\u0441\u044b\u0411\u043e\u043d\u0443\u0441\u0421\u0442\u0430\u0442\u044c\u044f \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u0430\u043d\u0430 \u043d\u0430 \u0447\u0438\u0442\u0430\u0442\u0435\u043b\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432 \u0446\u0435\u043b\u043e\u043c \u043f\u043e\u043d\u0438\u043c\u0430\u044e\u0442, \u043a\u0442\u043e, \u043a\u0430\u043a \u0438 \u0437\u0430\u0447\u0435\u043c \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u0442 \u0431\u044d\u043a\u0435\u043d\u0434.\u0421\u043f\u043e\u0439\u043b\u0435\u0440 \u041f\u043e-\u0445\u043e\u0440\u043e\u0448\u0435\u043c\u0443 \u0441\u0442\u043e\u0438\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0443\u044e \u0441\u0442\u0430\u0442\u044c\u044e \u0438 \u043d\u0430 \u044d\u0442\u0443 \u0442\u0435\u043c\u0443. \u0422\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 (WeatherForecast API)\u041d\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u2014 minimal ASP.NET Core API \u0441 \u0434\u0432\u0443\u043c\u044f \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430\u043c\u0438:POST \/weatherforecast\/generate \u2014 \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u043f\u0440\u043e\u0433\u043d\u043e\u0437 \u043f\u043e\u0433\u043e\u0434\u044b, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f TimeProvider, Random \u0438 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, appsettings.json), \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442 \u0435\u0433\u043e \u0432 PostgreSQL \u0447\u0435\u0440\u0435\u0437 EF Core.GET \/weatherforecast\/today \u2014 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043f\u0440\u043e\u0433\u043d\u043e\u0437 \u043d\u0430 \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u0438\u0437 \u0411\u0414.\u041f\u043e\u043b\u043d\u044b\u0439 \u043a\u043e\u0434 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0430 \u0411\u0414 PostgreSQL, \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430\u044f \u0447\u0435\u0440\u0435\u0437 ConnectionString \u0441 \u0438\u043c\u0435\u043d\u0435\u043c PgDb.using Microsoft.EntityFrameworkCore;namespace WebApiTestSubject;public class Program{    public const string ConnectionStringName = &#171;PgDb&#187;;    public static void Main(string[] args)    {        var builder = WebApplication.CreateBuilder(args);        builder.Services            .AddSingleton((_) =&gt; Random.Shared)            .AddSingleton((_) =&gt; TimeProvider.System)            .AddDbContext&lt;ApplicationDbContext&gt;((sp, options) =&gt;            {                var connStr = builder.Configuration.GetConnectionString(ConnectionStringName);                options.UseNpgsql(connStr);            });        var app = builder.Build();        app.MapPost(&#171;\/weatherforecast\/generate&#187;, async (TimeProvider tp, Random r, IConfiguration cfg, ApplicationDbContext dbCtx) =&gt;        {            var now = tp.GetUtcNow();            var date = DateOnly.FromDateTime(now.Date);            var temperature = r.Next(100);            var summary = cfg.GetValue&lt;string&gt;(&#171;summary&#187;);            var forecast = new WeatherForecast(date, temperature, summary);            dbCtx.WeatherForecasts.Add(new WeatherForecastEntity { Data = forecast });                        await dbCtx.SaveChangesAsync();        });        app.MapGet(&#171;\/weatherforecast\/today&#187;, async (TimeProvider tp, ApplicationDbContext dbCtx) =&gt;        {            var now = tp.GetUtcNow();            var today = DateOnly.FromDateTime(now.Date);            var entity = await dbCtx.WeatherForecasts                .Where(x =&gt; x.Data.Date == today)                .FirstOrDefaultAsync();                            return entity is null ? Results.NotFound() : Results.Ok(entity.Data);        });        app.Run();    }}public class ApplicationDbContext : DbContext{    public DbSet&lt;WeatherForecastEntity&gt; WeatherForecasts { get; init; }    public ApplicationDbContext(DbContextOptions&lt;ApplicationDbContext&gt; options)    : base(options)    {    }    protected override void OnModelCreating(ModelBuilder modelBuilder)    {        modelBuilder.Entity&lt;WeatherForecastEntity&gt;().ComplexProperty(e =&gt; e.Data);    }}public class WeatherForecastEntity{    public long Id { get; init; }    public required WeatherForecast Data { get; init; }}public record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary);\u041d\u0430\u0448\u0430 \u0446\u0435\u043b\u044c \u2014 \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0434\u0435\u0442\u0435\u0440\u043c\u0438\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u0442\u0435\u0441\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0432\u0435\u0441\u044c \u0446\u0438\u043a\u043b: \u043e\u0442 HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0434\u043e \u0437\u0430\u043f\u0438\u0441\u0438 \u0432 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445.\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0441\u043f\u043e\u0441\u043e\u0431\u0430 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f\u0427\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e:\u0421\u043e\u0437\u0434\u0430\u0442\u044c CustomWebApplicationFactory, \u0443\u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0432 \u043e\u0442 WebApplicationFactory&lt;Program&gt;.\u041f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c ConfigureWebHost, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c TimeProvider \u0438 Random \u043d\u0430 \u043c\u043e\u043a\u0438.\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u0443\u044e \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430, \u0438\u043d\u0430\u0447\u0435 \u0442\u0435\u0441\u0442\u044b \u0431\u0443\u0434\u0443\u0442 \u043c\u0435\u0448\u0430\u0442\u044c \u0434\u0440\u0443\u0433 \u0434\u0440\u0443\u0433\u0443.\u0421\u043e\u0437\u0434\u0430\u0442\u044c HttpClient \u0447\u0435\u0440\u0435\u0437 \u0444\u0430\u0431\u0440\u0438\u043a\u0443.\u041d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430 \u0434\u043b\u044f \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0432\u0441\u0435\u0433\u043e \u0432\u044b\u0448\u0435\u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u043d\u043e\u0433\u043e.\u041d\u0435 \u0437\u0430\u0431\u044b\u0442\u044c \u043f\u0440\u043e IAsyncDisposable \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0431\u0430\u0437\u044b \u0438 \u043e\u0447\u0438\u0441\u0442\u043a\u0438 \u0434\u0440\u0443\u0433\u0438\u0445 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u043f\u043e\u0441\u043b\u0435 \u0442\u0435\u0441\u0442\u0430.\u0418 \u044d\u0442\u043e \u2014 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u043e\u0434\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430. \u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u044c\u0442\u0435, \u0447\u0442\u043e \u0443 \u0432\u0430\u0441 \u0438\u0445 \u0434\u0435\u0441\u044f\u0442\u043e\u043a, \u0438 \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c WebApplicationFactory. \u041f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0438\u0435\u0440\u0430\u0440\u0445\u0438\u044f \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0445 \u043a\u043b\u0430\u0441\u0441\u043e\u0432, \u043a\u043e\u0434 \u043a\u043e\u043f\u0438\u0440\u0443\u0435\u0442\u0441\u044f. \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c \u0438 \u0447\u0438\u0442\u0430\u0442\u044c \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0432\u0441\u0451 \u0441\u043b\u043e\u0436\u043d\u0435\u0435. \u041e\u0442\u0434\u0435\u043b\u044c\u043d\u0430\u044f \u0431\u043e\u043b\u044c \u2014 \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0439 \u043a\u043e\u0434 \u0432 \u0434\u0440\u0443\u0433\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445.\u041a\u043e\u0434 \u0442\u0435\u0441\u0442\u043e\u0432, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435 \u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0430using System.Data.Common;using System.Net;using AwesomeAssertions;using AwesomeAssertions.Json;using FEFF.TestFixtures.AspNetCore.Randomness;using Microsoft.AspNetCore.Hosting;using Microsoft.AspNetCore.Mvc.Testing;using Microsoft.EntityFrameworkCore;using Microsoft.Extensions.Configuration;using Microsoft.Extensions.DependencyInjection;using Microsoft.Extensions.DependencyInjection.Extensions;using Microsoft.Extensions.Time.Testing;using Newtonsoft.Json.Linq;using WebApiTestSubject;namespace ExampleTests.AspNetCore.Basic;internal class CustomWebApplicationFactory : WebApplicationFactory&lt;Program&gt;{    public FakeTimeProvider FakeTime { get; } = new();    public FakeRandom FakeRandom { get; } = new();    protected override void ConfigureWebHost(IWebHostBuilder builder)    {        base.ConfigureWebHost(builder);                builder.ConfigureServices((ctx, _) =&gt;        {            ctx.Configuration.AddSuffixToConnectionString(&#171;PgDb&#187;, Guid.NewGuid().ToString());        });        builder.ConfigureServices(services =&gt;            services.TryReplaceSingleton&lt;TimeProvider&gt;(FakeTime)        );                builder.ConfigureServices(services =&gt;            services.TryReplaceSingleton&lt;Random&gt;(FakeRandom)        );        builder.UseSetting(&#171;summary&#187;, &#171;sunny&#187;);    }}public sealed class BasicApiTests : IAsyncLifetime{    internal CustomWebApplicationFactory App { get; }    internal HttpClient Client { get; }    internal AsyncServiceScope Scope { get; }    internal FakeTimeProvider AppTime =&gt; App.FakeTime;    internal FakeRandom AppRandom =&gt; App.FakeRandom;    internal ApplicationDbContext AppDbCtx =&gt; Scope.ServiceProvider.GetRequiredService&lt;ApplicationDbContext&gt;();    public BasicApiTests()    {        App = new();        Client = App.CreateClient();        \/\/ Application starts here        Scope = App.Services.CreateAsyncScope();    }    public async ValueTask DisposeAsync()    {        await AppDbCtx.Database.EnsureDeletedAsync(TestContext.Current.CancellationToken);        await Scope.DisposeAsync();        Client.Dispose();        await App.DisposeAsync();    }    public async ValueTask InitializeAsync()    {        await AppDbCtx.Database.EnsureCreatedAsync(TestContext.Current.CancellationToken);    }        #region tutorial: ASP.NET Core Application Testing    \/\/\/ &lt;summary&gt;    \/\/\/ Test: POST \/weatherforecast\/generate creates a forecast using time, random, and env var,    \/\/\/ persists it to the database, and GET \/weatherforecast\/today returns it.    \/\/\/    \/\/\/ This test verifies the full integration flow:    \/\/\/ 1. Configure fake time, fake random, and environment variable    \/\/\/ 2. POST to \/weatherforecast\/generate    \/\/\/ 3. Query the database directly to verify persistence    \/\/\/ 4. GET \/weatherforecast\/today to verify the API returns the persisted record    \/\/\/ &lt;\/summary&gt;    [Fact]    public async Task Example_Tutorial_Asp__Api__should_persist_and_return()    {        \/\/ Arrange        var expectedDate = &#171;2025-06-15&#187;;        var expectedTemperature = 42;        var expectedSummary = &#171;sunny&#187;;        AppTime.SetUtcNow(DateTimeOffset.Parse($&#187;{expectedDate}T12:00:00Z&#187;));        AppRandom.Int32Next = FixedNextStrategy.From(expectedTemperature);        \/\/ ACT        await PostAsync(Client, &#171;\/weatherforecast\/generate&#187;, null);        \/\/ Assert        var forecastEntities = await AppDbCtx.WeatherForecasts.ToListAsync(TestContext.Current.CancellationToken);        var forecasts = forecastEntities.Select(x =&gt; x.Data).ToList();        JToken.FromObject(forecasts)            .Should().BeEquivalentTo($$&#187;&#187;&#187;            [                {                    &#171;Date&#187;: &#171;{{expectedDate}}&#187;,                    &#171;TemperatureC&#187;: {{expectedTemperature}},                    &#171;Summary&#187;: &#171;{{expectedSummary}}&#187;,                }            ]            &#171;&#187;&#187;);        \/\/ ACT        var response = await GetAsync(Client, &#171;\/weatherforecast\/today&#187;);        \/\/ Assert        response            .Should().BeEquivalentTo(            $$&#187;&#187;&#187;            {                &#171;date&#187;: &#171;{{expectedDate}}&#187;,                &#171;temperatureC&#187;: {{expectedTemperature}},                &#171;summary&#187;: &#171;{{expectedSummary}}&#187;            }            &#171;&#187;&#187;);    }    # endregion    #region helpers    private static async Task&lt;JToken&gt; GetAsync(HttpClient client, string url)    {        var getResp = await client.GetAsync(url, TestContext.Current.CancellationToken);        var getBody = await getResp.Content.ReadAsStringAsync(TestContext.Current.CancellationToken);        getResp.StatusCode.Should().Be(HttpStatusCode.OK, getBody);        return JToken.Parse(getBody);    }    private static async Task PostAsync(HttpClient client, string url, string? data)    {        StringContent? sc = null;        if (data != null)            sc = new StringContent(data, System.Text.Encoding.UTF8,&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-482687","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/482687","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=482687"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/482687\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=482687"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=482687"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=482687"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}