{"id":339511,"date":"2022-10-10T21:00:30","date_gmt":"2022-10-10T21:00:30","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=339511"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=339511","title":{"rendered":"<span>Unit testing<\/span>"},"content":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<blockquote>\n<p>\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0434\u0443\u0440\u0430\u043a\u043e\u0432 \u0443\u043c\u0435\u043d\u044c\u0448\u0430\u0435\u0442\u0441\u044f, \u043d\u043e \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e \u0438\u0445 \u0440\u0430\u0441\u0442\u0435\u0442.<\/p>\n<p><strong><em>\u041c\u0438\u0445\u0430\u0438\u043b \u0413\u0435\u043d\u0438\u043d<\/em><\/strong><\/p>\n<\/blockquote>\n<h2>\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435<\/h2>\n<p>\u0425\u043e\u0442\u0435\u043b \u0431\u044b \u043e\u043f\u0443\u0431\u043b\u0438\u043a\u043e\u0432\u0430\u0442\u044c \u0441\u0442\u0430\u0442\u044c\u044e \u043e unit \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u0438\u0437 <a href=\"https:\/\/blowin.github.io\/blog\/posts\/2022\/10\/unit-testing\/\" rel=\"noopener noreferrer nofollow\">\u0441\u0432\u043e\u0435\u0433\u043e \u0431\u043b\u043e\u0433\u0430<\/a>. \u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0440\u0430\u0437\u0431\u0435\u0440\u0435\u043c\u0441\u044f \u0447\u0442\u043e \u0436\u0435 \u0442\u0430\u043a\u043e\u0435 unit \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435. C\u043e\u0433\u043b\u0430\u0441\u043d\u043e \u0432\u0438\u043a\u0438\u043f\u0435\u0434\u0438\u0438:<\/p>\n<p>\u041c\u043e\u0434\u0443\u043b\u044c\u043d\u043e\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435, \u0438\u043d\u043e\u0433\u0434\u0430 \u0431\u043b\u043e\u0447\u043d\u043e\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0438\u043b\u0438 \u044e\u043d\u0438\u0442-\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 (\u0430\u043d\u0433\u043b. unit testing) \u2014 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0432 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438, \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0449\u0438\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043d\u0430 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0441\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u043c\u043e\u0434\u0443\u043b\u0438 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b, \u043d\u0430\u0431\u043e\u0440\u044b \u0438\u0437 \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u043b\u0438 \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u044b\u0445 \u043c\u043e\u0434\u0443\u043b\u0435\u0439 \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0449\u0438\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438, \u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u0430\u043c\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438.<\/p>\n<p>\u0427\u0435\u0441\u0442\u043d\u043e \u0433\u043e\u0432\u043e\u0440\u044f, \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0438\u0437 \u0432\u0438\u043a\u0438\u043f\u0435\u0434\u0438\u0438 \u043a\u0430\u0436\u0435\u0442\u0441\u044f \u043c\u043d\u0435 \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0442\u043d\u044b\u043c. \u0422\u0430\u043a \u043a\u0430\u043a \u043d\u0435 \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442 \u0432\u0430\u0436\u043d\u043e\u0435 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u043e\u0432 \u043e\u0442 unit.<\/p>\n<p>\u042f \u0431\u044b \u0434\u0430\u043b \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0434\u043b\u044f unit \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f:<\/p>\n<p>Unit \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 &#8212; \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0432 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438, \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0449\u0438\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043d\u0430 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0441\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u043c\u043e\u0434\u0443\u043b\u0438 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0432 \u043e\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0439 \u043f\u0430\u043c\u044f\u0442\u0438 \u0438 \u043d\u0435 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u0442 \u0441 \u0432\u043d\u0435\u0448\u043d\u0438\u043c\u0438 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430\u043c\u0438 (\u0444\u0430\u0439\u043b\u043e\u0432\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430, \u0431\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445, \u0441\u0435\u0442\u044c \u0438 \u0442.\u0434).<\/p>\n<h2>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430<\/h2>\n<p>\u041f\u0440\u0435\u0434\u043f\u043e\u043b\u043e\u0436\u0438\u043c, \u0447\u0442\u043e \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u043c\u0435\u0435\u0442 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u0438 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0432 \u0444\u0430\u0439\u043b.<\/p>\n<pre><code class=\"cs\">public class UserRepository { \u00a0 \u00a0 private readonly string _path; \u00a0   \u00a0 \u00a0 private static readonly JsonSerializerOptions Op = new() \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 WriteIndented = true \u00a0 \u00a0 };  \u00a0 \u00a0 public UserRepository(string path) \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 _path = path ?? throw new ArgumentNullException(nameof(path)); \u00a0 \u00a0 }  \u00a0 \u00a0 public List&lt;User> GetAll() \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 using var stream = File.Open(_path, FileMode.Open); \u00a0 \u00a0 \u00a0 \u00a0 var itemList = JsonSerializer.Deserialize&lt;List&lt;User>>(stream, Op); \u00a0 \u00a0 \u00a0 \u00a0 return itemList ?? new List&lt;User>(); \u00a0 \u00a0 }  \u00a0 \u00a0 public void Save(List&lt;User> items) \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 using var stream = File.Open(_path, FileMode.Create); \u00a0 \u00a0 \u00a0 \u00a0 JsonSerializer.Serialize(stream, items, Op); \u00a0 \u00a0 } }<\/code><\/pre>\n<p>\u041c\u043e\u0434\u0435\u043b\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"cs\">public record User { \u00a0 \u00a0 public Guid Id { get; set; }  \u00a0 \u00a0 public string Name { get; set; }  \u00a0 \u00a0 public DateTime BirthDate { get; set; }  \u00a0 \u00a0 [JsonConstructor] \u00a0 \u00a0 public User(Guid id, string name, DateTime birthDate) \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 Id = id; \u00a0 \u00a0 \u00a0 \u00a0 Name = name; \u00a0 \u00a0 \u00a0 \u00a0 BirthDate = birthDate; \u00a0 \u00a0 } }<\/code><\/pre>\n<p>\u041d\u0430 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u043c\u044b \u043d\u0435 \u043c\u043e\u0436\u0435\u043c \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c unit \u0442\u0435\u0441\u0442\u044b \u043d\u0430 \u044d\u0442\u043e\u0442 \u043a\u043e\u0434, \u0442\u0430\u043a \u043a\u0430\u043a \u0432 \u043d\u0451\u043c \u0435\u0441\u0442\u044c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c \u043e\u0442 \u0444\u0430\u0439\u043b\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b.<\/p>\n<h2>\u0420\u0435\u0448\u0435\u043d\u0438\u0435<\/h2>\n<p>\u0415\u0441\u0442\u044c 2 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u0441\u043f\u043e\u0441\u043e\u0431\u0430 \u0438\u0437\u0431\u0430\u0432\u0438\u0442\u044c\u0441\u044f \u043e\u0442 \u044d\u0442\u043e\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438:<\/p>\n<p>1. \u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0444\u0430\u0439\u043b\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439 <strong>IFileProvider<\/strong>, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0431\u0443\u0434\u0443\u0442 \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438.<\/p>\n<p>2. \u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u043b\u0430\u0441\u0441 \u0441 \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0442\u043d\u044b\u043c \u043c\u0435\u0442\u043e\u0434\u043e\u043c \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u044f \u0444\u0430\u0439\u043b\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c <strong>Stream<\/strong>, \u0430 \u0443\u0436\u0435 \u043a\u0430\u0436\u0434\u044b\u0439 \u043d\u0430\u0441\u043b\u0435\u0434\u043d\u0438\u043a \u0431\u0443\u0434\u0435\u0442 \u0440\u0435\u0448\u0430\u0442\u044c \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c <strong>FileStream<\/strong>, <strong>MemoryStream<\/strong> \u0438\u043b\u0438 \u0447\u0442\u043e-\u0442\u043e \u0435\u0449\u0451.<\/p>\n<p>\u041b\u0438\u0447\u043d\u043e \u043c\u043d\u0435 \u0438\u043c\u043f\u043e\u043d\u0438\u0440\u0443\u0435\u0442 \u043f\u0435\u0440\u0432\u044b\u0439 \u043f\u043e\u0434\u0445\u043e\u0434, \u0442\u0430\u043a \u043a\u0430\u043a \u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043e\u043d \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0431\u043e\u043b\u0435\u0435 \u0433\u0438\u0431\u043a\u0438\u043c \u0438 \u043d\u0430\u043c \u043d\u0430\u0432\u0435\u0440\u043d\u044f\u043a\u0430 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0431\u043e\u043b\u044c\u0448\u0435 1 \u043c\u0435\u0442\u043e\u0434\u0430 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0444\u0430\u0439\u043b\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439, \u0438 \u0443\u0434\u043e\u0431\u043d\u0435\u0435 \u0431\u0443\u0434\u0435\u0442 \u0438\u043c\u0435\u0442\u044c \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442.<\/p>\n<h3>IFileProvider<\/h3>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043e\u043f\u0438\u0448\u0435\u043c <strong>IFileProvider<\/strong>.<\/p>\n<pre><code class=\"cs\">public interface IFileProvider { \u00a0 \u00a0 Stream Open(string path, FileMode mode); }<\/code><\/pre>\n<p>\u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0431\u0443\u0434\u0435\u0442 \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442 &#8212; \u044d\u0442\u043e 1 \u043c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u044f \u0444\u0430\u0439\u043b\u0430.<\/p>\n<p>\u0421\u0440\u0430\u0437\u0443 \u0436\u0435 \u043f\u0438\u0448\u0435\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0439 \u0444\u0430\u0439\u043b\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b:<\/p>\n<pre><code class=\"cs\">public class PhysicianFileProvider : IFileProvider { \u00a0 \u00a0 public Stream Open(string path, FileMode mode)  \u00a0 \u00a0 \u00a0 => new FileStream(path, mode); }<\/code><\/pre>\n<h4>\u0414\u043e\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439<\/h4>\n<p>\u0418\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0443\u0442 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u043c\u0438, \u0437\u0430\u043c\u0435\u043d\u044f\u0435\u043c 2 \u0432\u044b\u0437\u043e\u0432\u0430 <strong>File.Open<\/strong>, \u043d\u0430 \u0432\u044b\u0437\u043e\u0432 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430.<\/p>\n<pre><code class=\"cs\">public class UserRepository { \u00a0 \u00a0 private readonly string _path; \u00a0 \u00a0 private readonly IFileProvider _fileProvider; \u00a0 \u00a0 private static readonly JsonSerializerOptions Op = new() \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 WriteIndented = true \u00a0 \u00a0 };  \u00a0 \u00a0 \/\/ \u041d\u043e\u0432\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0432 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u2193 \u00a0 \u00a0 public UserRepository(string path, IFileProvider? fileProvider = null) \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 _path = path ?? throw new ArgumentNullException(nameof(path)); \u00a0 \u00a0 \u00a0 \u00a0 _fileProvider = fileProvider ?? new PhysicianFileProvider(); \u00a0 \u00a0 }  \u00a0 \u00a0 public List&lt;User> GetAll() \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 \/\/ \u041c\u0435\u043d\u044f\u0435\u043c \u0432\u044b\u0437\u043e\u0432 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u2193 \u00a0 \u00a0 \u00a0 \u00a0 using var stream = _fileProvider.Open(_path, FileMode.Open); \u00a0 \u00a0 \u00a0 \u00a0 var itemList = JsonSerializer.Deserialize&lt;List&lt;User>>(stream, Op); \u00a0 \u00a0 \u00a0 \u00a0 return itemList ?? new List&lt;User>(); \u00a0 \u00a0 }  \u00a0 \u00a0 public void Save(List&lt;User> items) \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 \/\/ \u041c\u0435\u043d\u044f\u0435\u043c \u0432\u044b\u0437\u043e\u0432 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u2193 \u00a0 \u00a0 \u00a0 \u00a0 using var stream = _fileProvider.Open(_path, FileMode.Create); \u00a0 \u00a0 \u00a0 \u00a0 JsonSerializer.Serialize(stream, items, Op); \u00a0 \u00a0 } }<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0442\u0430\u043a\u0438\u0445 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0445 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439, \u043c\u044b \u0441\u043c\u043e\u0436\u0435\u043c \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u0444\u0430\u0439\u043b\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439, \u043d\u0430 \u043e\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u0443\u044e \u043f\u0430\u043c\u044f\u0442\u044c, \u0441\u0435\u0442\u044c \u0438\u043b\u0438 \u043b\u044e\u0431\u043e\u0439 \u0434\u0440\u0443\u0433\u043e\u0439 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f.<\/p>\n<h3>\u041f\u0438\u0448\u0435\u043c \u0442\u0435\u0441\u0442\u044b<\/h3>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <a href=\"https:\/\/xunit.net\/\" rel=\"noopener noreferrer nofollow\">xUnit<\/a>. \u042d\u0442\u043e \u0434\u0435\u043b\u043e \u0432\u043a\u0443\u0441\u0430, \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043b\u044e\u0431\u0443\u044e \u0434\u0440\u0443\u0433\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u0434\u043b\u044f Unit \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f.<\/p>\n<p>\u0414\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0442\u0435\u0441\u0442\u043e\u0432, \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0443\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e <strong>IFileProvider<\/strong>.<\/p>\n<pre><code class=\"cs\">public sealed class MemoryFileProvider : IFileProvider { \u00a0 \u00a0 private readonly NonDisposableMemoryStream _memStream = new(); \u00a0 \u00a0      public Stream Open(string path, FileMode mode) \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 _memStream.Position = 0; \u00a0 \u00a0 \u00a0 \u00a0 return _memStream; \u00a0 \u00a0 }  \u00a0 \u00a0 public void Delete(string path) { }  \u00a0 \u00a0 private sealed class NonDisposableMemoryStream : MemoryStream \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 protected override void Dispose(bool disposing) {} \u00a0 \u00a0 } }<\/code><\/pre>\n<p>\u0412 \u0440\u0430\u043c\u043a\u0430\u0445 \u0442\u0435\u0441\u0442\u0430 \u0443 \u043d\u0430\u0441 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c\u0441\u044f 1 \u0444\u0430\u0439\u043b, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 1 \u043f\u043e\u043b\u0435\u043c, \u043d\u043e \u0434\u043b\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u0442\u0435\u0441\u0442\u043e\u0432, \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <strong>MemoryStream<\/strong> \u0432 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438 <strong>using<\/strong>, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u043d\u0430\u0441\u043b\u0435\u0434\u043d\u0438\u043a\u0430 \u0438 \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u043c\u0435\u0442\u043e\u0434 <strong>Dispose<\/strong>.<\/p>\n<p>\u0414\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0435\u0449\u0451 2 \u043c\u0435\u0442\u043e\u0434\u0430 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f \u0434\u043b\u044f <strong>IFileProvider<\/strong>, \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u043e\u0436\u0438\u0442\u044c \u0432 \u043d\u0435\u0433\u043e json \u0438 \u0434\u043e\u0441\u0442\u0430\u0442\u044c. \u0418\u0445 \u043c\u043e\u0436\u043d\u043e \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432 \u0441\u0431\u043e\u0440\u043a\u0435 unit \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<pre><code class=\"cs\">public static string ToJson(this IFileProvider self, string path) { \u00a0 \u00a0 var stream = self.Open(path, FileMode.Open); \u00a0 \u00a0 using var streamReader = new StreamReader(stream); \u00a0 \u00a0 return streamReader.ReadToEnd(); }  public static void WithJson(this IFileProvider self, string path, string json) { \u00a0 \u00a0 var stream = self.Open(path, FileMode.OpenOrCreate); \u00a0 \u00a0 using var writer = new StreamWriter(stream); \u00a0 \u00a0 writer.Write(json); }<\/code><\/pre>\n<h4>MemberData<\/h4>\n<p>\u0414\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0432\u044b\u0433\u043b\u044f\u0434\u044f\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c<\/p>\n<pre><code class=\"cs\">public static IEnumerable&lt;object[]> TestData { \u00a0 \u00a0 get \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 return new List&lt;object[]> \u00a0 \u00a0 \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 new object[] \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 @\"[]\", \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 new List&lt;User>() \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 },  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 new object[] \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 @\"[ { \"\"Id\"\": \"\"2d445f82-004f-4b91-ba16-4bfcd24d96e8\"\", \"\"Name\"\": \"\"Test\"\", \"\"BirthDate\"\": \"\"2022-10-10T00:00:00\"\" } ]\", \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 new List&lt;User> \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 new(Guid.Parse(\"2d445f82-004f-4b91-ba16-4bfcd24d96e8\"),  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \"Test\",  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 new DateTime(2022, 10, 10)) \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 } \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 }, \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 new object[] \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 @\"[ { \"\"Id\"\": \"\"2d445f82-004f-4b91-ba16-4bfcd24d96e8\"\", \"\"Name\"\": \"\"Test\"\", \"\"BirthDate\"\": \"\"2022-10-10T00:00:00\"\" }, { \"\"Id\"\": \"\"2d445f82-004f-4b91-ba16-4bfcd24d96e9\"\", \"\"Name\"\": \"\"Test 2\"\", \"\"BirthDate\"\": \"\"2021-10-10T00:00:00\"\" } ]\", \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 new List&lt;User> \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 new(Guid.Parse(\"2d445f82-004f-4b91-ba16-4bfcd24d96e8\"),  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \"Test\",  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 new DateTime(2022, 10, 10)), \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 new(Guid.Parse(\"2d445f82-004f-4b91-ba16-4bfcd24d96e9\"),  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \"Test 2\",  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 new DateTime(2021, 10, 10)), \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 } \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 }, \u00a0 \u00a0 \u00a0 \u00a0 }; \u00a0 \u00a0 } }<\/code><\/pre>\n<h4>\u041c\u0435\u0442\u043e\u0434 Save<\/h4>\n<pre><code class=\"cs\">[Theory] [MemberData(nameof(TestData))] public void Save(string expect, List&lt;User> items) { \u00a0 \u00a0 \/\/ Arrange \u00a0 \u00a0 const string filePath = \"tmp.json\"; \u00a0 \u00a0 var fileProvider = new MemoryFileProvider(); \u00a0 \u00a0 var repository = new UserRepository(filePath, fileProvider);  \u00a0 \u00a0 \/\/ Act \u00a0 \u00a0 repository.Save(items); \u00a0 \u00a0 \/\/ \u0412\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u043c\u0435\u0442\u043e\u0434 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435, \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 json \u00a0 \u00a0 var json = fileProvider.ToJson(filePath);  \u00a0 \u00a0 \/\/ Assert \u00a0 \u00a0 Assert.Equal(expect, json); }<\/code><\/pre>\n<h4>\u041c\u0435\u0442\u043e\u0434 GetAll<\/h4>\n<p>\u041e\u043d \u0431\u0443\u0434\u0435\u0442 \u043e\u0447\u0435\u043d\u044c \u043f\u043e\u0445\u043e\u0436 \u043d\u0430 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043c\u0435\u0442\u043e\u0434\u0430 <strong>Save<\/strong>, \u043c\u043e\u0436\u043d\u043e \u0434\u0430\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <strong>MemberData<\/strong> \u0438\u0437 \u043f\u0440\u043e\u0448\u043b\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430.<\/p>\n<pre><code class=\"cs\">[Theory] [MemberData(nameof(TestData))] public void GetAll(string initialValue, List&lt;User> expect) { \u00a0 \u00a0 \/\/ Arrange \u00a0 \u00a0 const string filePath = \"tmp.json\"; \u00a0 \u00a0 var fileProvider = new MemoryFileProvider();  \u00a0 \u00a0 \/\/ \u0417\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0441\u0442\u0440\u0438\u043c, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u043c\u0435\u0442\u043e\u0434 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 \u00a0 \u00a0 fileProvider.WithJson(filePath, initialValue); \u00a0 \u00a0 var repository = new UserRepository(filePath, fileProvider);  \u00a0 \u00a0 \/\/ Act \u00a0 \u00a0 var result = repository.GetAll();  \u00a0 \u00a0 \/\/ Assert \u00a0 \u00a0 Assert.Equal(expect, result); }<\/code><\/pre>\n<h2>\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435<\/h2>\n<p>\u0421 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0435\u0439 \u043c\u043e\u0436\u043d\u043e \u043b\u0435\u0433\u043a\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b \u043d\u0430 \u043d\u0430\u0448 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u043c 1 \u043c\u0435\u0442\u043e\u0434 \u0432 <strong>IFileProvider<\/strong><\/p>\n<pre><code class=\"cs\">public interface IFileProvider { \u00a0 \u00a0 Stream Open(string path, FileMode mode); \u00a0 \u00a0 void Delete(string path); }<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043c\u0435\u0442\u043e\u0434 \u0432 <strong>PhysicianFileProvider<\/strong><\/p>\n<pre><code class=\"cs\">public void Delete(string path) => File.Delete(path);<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043c\u0435\u0442\u043e\u0434 \u0432 <strong>MemoryFileProvider<\/strong><\/p>\n<pre><code class=\"cs\">public void Delete(string path) {}<\/code><\/pre>\n<p>\u0414\u043e\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u043d\u0430\u0448\u0438 \u0442\u0435\u0441\u0442\u044b, \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043e\u0431\u0435\u0440\u043d\u0435\u043c \u0432\u0435\u0441\u044c \u043a\u043e\u0434 \u0432 try finally, \u0447\u0442\u043e\u0431\u044b \u0443\u0434\u0430\u043b\u044f\u0442\u044c \u0444\u0430\u0439\u043b \u043f\u043e \u043e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044e \u0442\u0435\u0441\u0442\u0430. \u041f\u0440\u0438\u0432\u0435\u0434\u0443 \u043f\u0440\u0438\u043c\u0435\u0440 \u0442\u0435\u0441\u0442\u0430 \u043c\u0435\u0442\u043e\u0434\u0430 <strong>Save<\/strong>, \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0432\u043e \u0432\u0442\u043e\u0440\u043e\u043c \u0431\u0443\u0434\u0443\u0442 \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u044b:<\/p>\n<pre><code class=\"cs\">[Theory] [MemberData(nameof(TestData))] public void Save(string expect, List&lt;User> items) { \u00a0 \u00a0 \/\/ Arrange \u00a0 \u00a0 const string filePath = \"tmp.json\"; \u00a0 \u00a0 var fileProvider = new MemoryFileProvider();  \u00a0 \u00a0 try \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 var repository = new UserRepository(filePath, fileProvider);  \u00a0 \u00a0 \u00a0 \u00a0 \/\/ Act \u00a0 \u00a0 \u00a0 \u00a0 repository.Save(items); \u00a0 \u00a0 \u00a0 \u00a0 var json = fileProvider.ToJson(filePath); \u00a0   \u00a0 \u00a0 \u00a0 \u00a0 \/\/ Assert \u00a0 \u00a0 \u00a0 \u00a0 Assert.Equal(expect, json); \u00a0 \u00a0 } \u00a0 \u00a0 finally \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 try \u00a0 \u00a0 \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \/\/ \u0423\u0434\u0430\u043b\u044f\u0435\u043c \u0444\u0430\u0439\u043b \u043f\u043e \u043e\u043a\u043e\u0447\u0430\u043d\u0438\u044e \u0442\u0435\u0441\u0442\u0430 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 fileProvider.Delete(filePath); \u00a0 \u00a0 \u00a0 \u00a0 } \u00a0 \u00a0 \u00a0 \u00a0 catch {} \u00a0 \u00a0 } }<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c <strong>new MemoryFileProvider()<\/strong> \u043d\u0430 <strong>new PhysicianFileProvider()<\/strong>, \u0442\u043e\u0433\u0434\u0430 \u0442\u0435\u0441\u0442 \u0431\u0443\u0434\u0435\u0442 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441 \u0444\u0430\u0439\u043b\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439. \u041f\u0440\u0438 \u0436\u0435\u043b\u0430\u043d\u0438\u0438 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u043d\u0443\u0436\u043d\u0443\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e <strong>IFileProvider<\/strong> \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430 \u0442\u0435\u0441\u0442\u0430.<\/p>\n<h2>\u0418\u0442\u043e\u0433<\/h2>\n<p>\u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043a\u043e\u0434\u0430 \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u043f\u043e\u043c\u043e\u0447\u044c \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u043a\u043b\u0430\u0441\u0441\u0430 \u0438 \u0443\u043b\u0443\u0447\u0448\u0438\u0442\u044c \u0434\u0438\u0437\u0430\u0439\u043d. \u041f\u043e\u0440\u043e\u0439 \u044d\u0442\u043e \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u043a \u0438\u0437\u0431\u044b\u0442\u043e\u0447\u043d\u043e\u0441\u0442\u0438, \u043d\u043e \u044d\u0442\u043e \u0441\u043b\u0438\u0445\u0432\u043e\u0439 \u043f\u043e\u043a\u0440\u044b\u0432\u0435\u0442\u0441\u044f \u043d\u0430\u0434\u0451\u0436\u043d\u043e\u0441\u0442\u044c\u044e \u043a\u043e\u0434\u0430.<\/p>\n<p>\u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e, \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438\u0434\u0451\u0442 \u043a\u0443\u0434\u0430 \u0431\u044b\u0441\u0442\u0440\u0435\u0435, \u0442\u0430\u043a \u043a\u0430\u043a \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 \u043c\u0435\u043d\u044c\u0448\u0435 \u0431\u043e\u044f\u0442\u0441\u044f \u043f\u043e\u043b\u043e\u043c\u0430\u0442\u044c \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043e\u0447\u0435\u0440\u0435\u0434\u043d\u044b\u043c \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433\u043e\u043c \u0438\u043b\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435\u043c \u043d\u043e\u0432\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438.<\/p>\n<h2>\u0421\u0441\u044b\u043b\u043a\u0438<\/h2>\n<ul>\n<li>\n<p><a href=\"https:\/\/blowin.github.io\/blog\/\" rel=\"noopener noreferrer nofollow\">\u041c\u043e\u0439 \u0431\u043b\u043e\u0433<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%82%D0%B5%D1%81%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5\" rel=\"noopener noreferrer nofollow\">\u041c\u043e\u0434\u0443\u043b\u044c\u043d\u043e\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 (Wiki)<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/xunit.net\/\" rel=\"noopener noreferrer nofollow\">xUnit<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/habr.com\/ru\/post\/169381\/\" rel=\"noopener noreferrer nofollow\">\u042e\u043d\u0438\u0442-\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0434\u043b\u044f \u0447\u0430\u0439\u043d\u0438\u043a\u043e\u0432 (\u0425\u0430\u0431\u0440)<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/learn.microsoft.com\/ru-ru\/dotnet\/core\/testing\/\" rel=\"noopener noreferrer nofollow\">\u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432 .NET (Microsoft)<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/learn.microsoft.com\/ru-ru\/dotnet\/core\/testing\/unit-testing-best-practices\" rel=\"noopener noreferrer nofollow\">\u0420\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0430\u0446\u0438\u0438 \u043f\u043e \u043c\u043e\u0434\u0443\u043b\u044c\u043d\u043e\u043c\u0443 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e \u0434\u043b\u044f .NET Core \u0438 .NET Standard (Microsoft)<\/a><\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"v-portal\" style=\"display:none;\"><\/div>\n<\/div>\n<p> <!----> <!----><br \/> \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\/post\/692456\/\"> https:\/\/habr.com\/ru\/post\/692456\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<blockquote>\n<p>\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0434\u0443\u0440\u0430\u043a\u043e\u0432 \u0443\u043c\u0435\u043d\u044c\u0448\u0430\u0435\u0442\u0441\u044f, \u043d\u043e \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e \u0438\u0445 \u0440\u0430\u0441\u0442\u0435\u0442.<\/p>\n<p><strong><em>\u041c\u0438\u0445\u0430\u0438\u043b \u0413\u0435\u043d\u0438\u043d<\/em><\/strong><\/p>\n<\/blockquote>\n<h2>\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435<\/h2>\n<p>\u0425\u043e\u0442\u0435\u043b \u0431\u044b \u043e\u043f\u0443\u0431\u043b\u0438\u043a\u043e\u0432\u0430\u0442\u044c \u0441\u0442\u0430\u0442\u044c\u044e \u043e unit \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u0438\u0437 <a href=\"https:\/\/blowin.github.io\/blog\/posts\/2022\/10\/unit-testing\/\" rel=\"noopener noreferrer nofollow\">\u0441\u0432\u043e\u0435\u0433\u043e \u0431\u043b\u043e\u0433\u0430<\/a>. \u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0440\u0430\u0437\u0431\u0435\u0440\u0435\u043c\u0441\u044f \u0447\u0442\u043e \u0436\u0435 \u0442\u0430\u043a\u043e\u0435 unit \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435. C\u043e\u0433\u043b\u0430\u0441\u043d\u043e \u0432\u0438\u043a\u0438\u043f\u0435\u0434\u0438\u0438:<\/p>\n<p>\u041c\u043e\u0434\u0443\u043b\u044c\u043d\u043e\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435, \u0438\u043d\u043e\u0433\u0434\u0430 \u0431\u043b\u043e\u0447\u043d\u043e\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0438\u043b\u0438 \u044e\u043d\u0438\u0442-\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 (\u0430\u043d\u0433\u043b. unit testing) \u2014 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0432 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438, \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0449\u0438\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043d\u0430 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0441\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u043c\u043e\u0434\u0443\u043b\u0438 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b, \u043d\u0430\u0431\u043e\u0440\u044b \u0438\u0437 \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u043b\u0438 \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u044b\u0445 \u043c\u043e\u0434\u0443\u043b\u0435\u0439 \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0449\u0438\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438, \u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u0430\u043c\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438.<\/p>\n<p>\u0427\u0435\u0441\u0442\u043d\u043e \u0433\u043e\u0432\u043e\u0440\u044f, \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0438\u0437 \u0432\u0438\u043a\u0438\u043f\u0435\u0434\u0438\u0438 \u043a\u0430\u0436\u0435\u0442\u0441\u044f \u043c\u043d\u0435 \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0442\u043d\u044b\u043c. \u0422\u0430\u043a \u043a\u0430\u043a \u043d\u0435 \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442 \u0432\u0430\u0436\u043d\u043e\u0435 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u043e\u0432 \u043e\u0442 unit.<\/p>\n<p>\u042f \u0431\u044b \u0434\u0430\u043b \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0434\u043b\u044f unit \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f:<\/p>\n<p>Unit \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 &#8212; \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0432 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438, \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0449\u0438\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043d\u0430 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0441\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u043c\u043e\u0434\u0443\u043b\u0438 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0432 \u043e\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0439 \u043f\u0430\u043c\u044f\u0442\u0438 \u0438 \u043d\u0435 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u0442 \u0441 \u0432\u043d\u0435\u0448\u043d\u0438\u043c\u0438 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430\u043c\u0438 (\u0444\u0430\u0439\u043b\u043e\u0432\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430, \u0431\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445, \u0441\u0435\u0442\u044c \u0438 \u0442.\u0434).<\/p>\n<h2>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430<\/h2>\n<p>\u041f\u0440\u0435\u0434\u043f\u043e\u043b\u043e\u0436\u0438\u043c, \u0447\u0442\u043e \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u043c\u0435\u0435\u0442 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u0438 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0432 \u0444\u0430\u0439\u043b.<\/p>\n<pre><code class=\"cs\">public class UserRepository { \u00a0 \u00a0 private readonly string _path; \u00a0   \u00a0 \u00a0 private static readonly JsonSerializerOptions Op = new() \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 WriteIndented = true \u00a0 \u00a0 };  \u00a0 \u00a0 public UserRepository(string path) \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 _path = path ?? throw new ArgumentNullException(nameof(path)); \u00a0 \u00a0 }  \u00a0 \u00a0 public List&lt;User> GetAll() \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 using var stream = File.Open(_path, FileMode.Open); \u00a0 \u00a0 \u00a0 \u00a0 var itemList = JsonSerializer.Deserialize&lt;List&lt;User>>(stream, Op); \u00a0 \u00a0 \u00a0 \u00a0 return itemList ?? new List&lt;User>(); \u00a0 \u00a0 }  \u00a0 \u00a0 public void Save(List&lt;User> items) \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 using var stream = File.Open(_path, FileMode.Create); \u00a0 \u00a0 \u00a0 \u00a0 JsonSerializer.Serialize(stream, items, Op); \u00a0 \u00a0 } }<\/code><\/pre>\n<p>\u041c\u043e\u0434\u0435\u043b\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"cs\">public record User { \u00a0 \u00a0 public Guid Id { get; set; }  \u00a0 \u00a0 public string Name { get; set; }  \u00a0 \u00a0 public DateTime BirthDate { get; set; }  \u00a0 \u00a0 [JsonConstructor] \u00a0 \u00a0 public User(Guid id, string name, DateTime birthDate) \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 Id = id; \u00a0 \u00a0 \u00a0 \u00a0 Name = name; \u00a0 \u00a0 \u00a0 \u00a0 BirthDate = birthDate; \u00a0 \u00a0 } }<\/code><\/pre>\n<p>\u041d\u0430 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u043c\u044b \u043d\u0435 \u043c\u043e\u0436\u0435\u043c \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c unit \u0442\u0435\u0441\u0442\u044b \u043d\u0430 \u044d\u0442\u043e\u0442 \u043a\u043e\u0434, \u0442\u0430\u043a \u043a\u0430\u043a \u0432 \u043d\u0451\u043c \u0435\u0441\u0442\u044c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c \u043e\u0442 \u0444\u0430\u0439\u043b\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b.<\/p>\n<h2>\u0420\u0435\u0448\u0435\u043d\u0438\u0435<\/h2>\n<p>\u0415\u0441\u0442\u044c 2 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u0441\u043f\u043e\u0441\u043e\u0431\u0430 \u0438\u0437\u0431\u0430\u0432\u0438\u0442\u044c\u0441\u044f \u043e\u0442 \u044d\u0442\u043e\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438:<\/p>\n<p>1. \u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0444\u0430\u0439\u043b\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439 <strong>IFileProvider<\/strong>, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0431\u0443\u0434\u0443\u0442 \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438.<\/p>\n<p>2. \u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u043b\u0430\u0441\u0441 \u0441 \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0442\u043d\u044b\u043c \u043c\u0435\u0442\u043e\u0434\u043e\u043c \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u044f \u0444\u0430\u0439\u043b\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c <strong>Stream<\/strong>, \u0430 \u0443\u0436\u0435 \u043a\u0430\u0436\u0434\u044b\u0439 \u043d\u0430\u0441\u043b\u0435\u0434\u043d\u0438\u043a \u0431\u0443\u0434\u0435\u0442 \u0440\u0435\u0448\u0430\u0442\u044c \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c <strong>FileStream<\/strong>, <strong>MemoryStream<\/strong> \u0438\u043b\u0438 \u0447\u0442\u043e-\u0442\u043e \u0435\u0449\u0451.<\/p>\n<p>\u041b\u0438\u0447\u043d\u043e \u043c\u043d\u0435 \u0438\u043c\u043f\u043e\u043d\u0438\u0440\u0443\u0435\u0442 \u043f\u0435\u0440\u0432\u044b\u0439 \u043f\u043e\u0434\u0445\u043e\u0434, \u0442\u0430\u043a \u043a\u0430\u043a \u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043e\u043d \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0431\u043e\u043b\u0435\u0435 \u0433\u0438\u0431\u043a\u0438\u043c \u0438 \u043d\u0430\u043c \u043d\u0430\u0432\u0435\u0440\u043d\u044f\u043a\u0430 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0431\u043e\u043b\u044c\u0448\u0435 1 \u043c\u0435\u0442\u043e\u0434\u0430 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0444\u0430\u0439\u043b\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439, \u0438 \u0443\u0434\u043e\u0431\u043d\u0435\u0435 \u0431\u0443\u0434\u0435\u0442 \u0438\u043c\u0435\u0442\u044c \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442.<\/p>\n<h3>IFileProvider<\/h3>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043e\u043f\u0438\u0448\u0435\u043c <strong>IFileProvider<\/strong>.<\/p>\n<pre><code class=\"cs\">public interface IFileProvider { \u00a0 \u00a0 Stream Open(string path, FileMode mode); }<\/code><\/pre>\n<p>\u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0431\u0443\u0434\u0435\u0442 \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442 &#8212; \u044d\u0442\u043e 1 \u043c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u044f \u0444\u0430\u0439\u043b\u0430.<\/p>\n<p>\u0421\u0440\u0430\u0437\u0443 \u0436\u0435 \u043f\u0438\u0448\u0435\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0439 \u0444\u0430\u0439\u043b\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b:<\/p>\n<pre><code class=\"cs\">public class PhysicianFileProvider : IFileProvider { \u00a0 \u00a0 public Stream Open(string path, FileMode mode)  \u00a0 \u00a0 \u00a0 => new FileStream(path, mode); }<\/code><\/pre>\n<h4>\u0414\u043e\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439<\/h4>\n<p>\u0418\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0443\u0442 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u043c\u0438, \u0437\u0430\u043c\u0435\u043d\u044f\u0435\u043c 2 \u0432\u044b\u0437\u043e\u0432\u0430 <strong>File.Open<\/strong>, \u043d\u0430 \u0432\u044b\u0437\u043e\u0432 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430.<\/p>\n<pre><code class=\"cs\">public class UserRepository { \u00a0 \u00a0 private readonly string _path; \u00a0 \u00a0 private readonly IFileProvider _fileProvider; \u00a0 \u00a0 private static readonly JsonSerializerOptions Op = new() \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 WriteIndented = true \u00a0 \u00a0 };  \u00a0 \u00a0 \/\/ \u041d\u043e\u0432\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0432 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u2193 \u00a0 \u00a0 public UserRepository(string path, IFileProvider? fileProvider = null) \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 _path = path ?? throw new ArgumentNullException(nameof(path)); \u00a0 \u00a0 \u00a0 \u00a0 _fileProvider = fileProvider ?? new PhysicianFileProvider(); \u00a0 \u00a0 }  \u00a0 \u00a0 public List&lt;User> GetAll() \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 \/\/ \u041c\u0435\u043d\u044f\u0435\u043c \u0432\u044b\u0437\u043e\u0432 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u2193 \u00a0 \u00a0 \u00a0 \u00a0 using var stream = _fileProvider.Open(_path, FileMode.Open); \u00a0 \u00a0 \u00a0 \u00a0 var itemList = JsonSerializer.Deserialize&lt;List&lt;User>>(stream, Op); \u00a0 \u00a0 \u00a0 \u00a0 return itemList ?? new List&lt;User>(); \u00a0 \u00a0 }  \u00a0 \u00a0 public void Save(List&lt;User> items) \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 \/\/ \u041c\u0435\u043d\u044f\u0435\u043c \u0432\u044b\u0437\u043e\u0432 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u2193 \u00a0 \u00a0 \u00a0 \u00a0 using var stream = _fileProvider.Open(_path, FileMode.Create); \u00a0 \u00a0 \u00a0 \u00a0 JsonSerializer.Serialize(stream, items, Op); \u00a0 \u00a0 } }<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0442\u0430\u043a\u0438\u0445 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0445 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439, \u043c\u044b \u0441\u043c\u043e\u0436\u0435\u043c \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u0444\u0430\u0439\u043b\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439, \u043d\u0430 \u043e\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u0443\u044e \u043f\u0430\u043c\u044f\u0442\u044c, \u0441\u0435\u0442\u044c \u0438\u043b\u0438 \u043b\u044e\u0431\u043e\u0439 \u0434\u0440\u0443\u0433\u043e\u0439 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f.<\/p>\n<h3>\u041f\u0438\u0448\u0435\u043c \u0442\u0435\u0441\u0442\u044b<\/h3>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <a href=\"https:\/\/xunit.net\/\" rel=\"noopener noreferrer nofollow\">xUnit<\/a>. \u042d\u0442\u043e \u0434\u0435\u043b\u043e \u0432\u043a\u0443\u0441\u0430, \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043b\u044e\u0431\u0443\u044e \u0434\u0440\u0443\u0433\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u0434\u043b\u044f Unit \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f.<\/p>\n<p>\u0414\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0442\u0435\u0441\u0442\u043e\u0432, \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0443\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e <strong>IFileProvider<\/strong>.<\/p>\n<pre><code class=\"cs\">public sealed class MemoryFileProvider : IFileProvider { \u00a0 \u00a0 private readonly NonDisposableMemoryStream _memStream = new(); \u00a0 \u00a0      public Stream Open(string path, FileMode mode) \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 _memStream.Position = 0; \u00a0 \u00a0 \u00a0 \u00a0 return _memStream; \u00a0 \u00a0 }  \u00a0 \u00a0 public void Delete(string path) { }  \u00a0 \u00a0 private sealed class NonDisposableMemoryStream : MemoryStream \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 protected override void Dispose(bool disposing) {} \u00a0 \u00a0 } }<\/code><\/pre>\n<p>\u0412 \u0440\u0430\u043c\u043a\u0430\u0445 \u0442\u0435\u0441\u0442\u0430 \u0443 \u043d\u0430\u0441 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c\u0441\u044f 1 \u0444\u0430\u0439\u043b, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 1 \u043f\u043e\u043b\u0435\u043c, \u043d\u043e \u0434\u043b\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u0442\u0435\u0441\u0442\u043e\u0432, \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <strong>MemoryStream<\/strong> \u0432 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438 <strong>using<\/strong>, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u043d\u0430\u0441\u043b\u0435\u0434\u043d\u0438\u043a\u0430 \u0438 \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u043c\u0435\u0442\u043e\u0434 <strong>Dispose<\/strong>.<\/p>\n<p>\u0414\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0435\u0449\u0451 2 \u043c\u0435\u0442\u043e\u0434\u0430 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f \u0434\u043b\u044f <strong>IFileProvider<\/strong>, \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u043e\u0436\u0438\u0442\u044c \u0432 \u043d\u0435\u0433\u043e json \u0438 \u0434\u043e\u0441\u0442\u0430\u0442\u044c. \u0418\u0445 \u043c\u043e\u0436\u043d\u043e \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432 \u0441\u0431\u043e\u0440\u043a\u0435 unit \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<pre><code class=\"cs\">public static string ToJson(this IFileProvider self, string path) { \u00a0 \u00a0 var stream = self.Open(path, FileMode.Open); \u00a0 \u00a0 using var streamReader = new StreamReader(stream); \u00a0 \u00a0 return streamReader.ReadToEnd(); }  public static void WithJson(this IFileProvider self, string path, string json) { \u00a0 \u00a0 var stream = self.Open(path, FileMode.OpenOrCreate); \u00a0 \u00a0 using var writer = new StreamWriter(stream); \u00a0 \u00a0 writer.Write(json); }<\/code><\/pre>\n<h4>MemberData<\/h4>\n<p>\u0414\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0432\u044b\u0433\u043b\u044f\u0434\u044f\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c<\/p>\n<pre><code class=\"cs\">public static IEnumerable&lt;object[]> TestData { \u00a0 \u00a0 get \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 return new List&lt;object[]> \u00a0 \u00a0 \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 new object[] \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 @\"[]\", \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 new List&lt;User>() \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 },  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 new object[] \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 @\"[ { \"\"Id\"\": \"\"2d445f82-004f-4b91-ba16-4bfcd24d96e8\"\", \"\"Name\"\": \"\"Test\"\", \"\"BirthDate\"\": \"\"2022-10-10T00:00:00\"\" } ]\", \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 new List&lt;User> \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 new(Guid.Parse(\"2d445f82-004f-4b91-ba16-4bfcd24d96e8\"),  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \"Test\",  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 new DateTime(2022, 10, 10)) \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 } \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 }, \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 new object[] \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 @\"[ { \"\"Id\"\": \"\"2d445f82-004f-4b91-ba16-4bfcd24d96e8\"\", \"\"Name\"\": \"\"Test\"\", \"\"BirthDate\"\": \"\"2022-10-10T00:00:00\"\" }, { \"\"Id\"\": \"\"2d445f82-004f-4b91-ba16-4bfcd24d96e9\"\", \"\"Name\"\": \"\"Test 2\"\", \"\"BirthDate\"\": \"\"2021-10-10T00:00:00\"\" } ]\", \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 new List&lt;User> \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 new(Guid.Parse(\"2d445f82-004f-4b91-ba16-4bfcd24d96e8\"),  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \"Test\",  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 new DateTime(2022, 10, 10)), \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 new(Guid.Parse(\"2d445f82-004f-4b91-ba16-4bfcd24d96e9\"),  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \"Test 2\",  \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 new DateTime(2021, 10, 10)), \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 } \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 }, \u00a0 \u00a0 \u00a0 \u00a0 }; \u00a0 \u00a0 } }<\/code><\/pre>\n<h4>\u041c\u0435\u0442\u043e\u0434 Save<\/h4>\n<pre><code class=\"cs\">[Theory] [MemberData(nameof(TestData))] public void Save(string expect, List&lt;User> items) { \u00a0 \u00a0 \/\/ Arrange \u00a0 \u00a0 const string filePath = \"tmp.json\"; \u00a0 \u00a0 var fileProvider = new MemoryFileProvider(); \u00a0 \u00a0 var repository = new UserRepository(filePath, fileProvider);  \u00a0 \u00a0 \/\/ Act \u00a0 \u00a0 repository.Save(items); \u00a0 \u00a0 \/\/ \u0412\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u043c\u0435\u0442\u043e\u0434 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435, \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 json \u00a0 \u00a0 var json = fileProvider.ToJson(filePath);  \u00a0 \u00a0 \/\/ Assert \u00a0 \u00a0 Assert.Equal(expect, json); }<\/code><\/pre>\n<h4>\u041c\u0435\u0442\u043e\u0434 GetAll<\/h4>\n<p>\u041e\u043d \u0431\u0443\u0434\u0435\u0442 \u043e\u0447\u0435\u043d\u044c \u043f\u043e\u0445\u043e\u0436 \u043d\u0430 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043c\u0435\u0442\u043e\u0434\u0430 <strong>Save<\/strong>, \u043c\u043e\u0436\u043d\u043e \u0434\u0430\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <strong>MemberData<\/strong> \u0438\u0437 \u043f\u0440\u043e\u0448\u043b\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430.<\/p>\n<pre><code class=\"cs\">[Theory] [MemberData(nameof(TestData))] public void GetAll(string initialValue, List&lt;User> expect) { \u00a0 \u00a0 \/\/ Arrange \u00a0 \u00a0 const string filePath = \"tmp.json\"; \u00a0 \u00a0 var fileProvider = new MemoryFileProvider();  \u00a0 \u00a0 \/\/ \u0417\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0441\u0442\u0440\u0438\u043c, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u043c\u0435\u0442\u043e\u0434 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 \u00a0 \u00a0 fileProvider.WithJson(filePath, initialValue); \u00a0 \u00a0 var repository = new UserRepository(filePath, fileProvider);  \u00a0 \u00a0 \/\/ Act \u00a0 \u00a0 var result = repository.GetAll();  \u00a0 \u00a0 \/\/ Assert \u00a0 \u00a0 Assert.Equal(expect, result); }<\/code><\/pre>\n<h2>\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435<\/h2>\n<p>\u0421 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0435\u0439 \u043c\u043e\u0436\u043d\u043e \u043b\u0435\u0433\u043a\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b \u043d\u0430 \u043d\u0430\u0448 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u043c 1 \u043c\u0435\u0442\u043e\u0434 \u0432 <strong>IFileProvider<\/strong><\/p>\n<pre><code class=\"cs\">public interface IFileProvider { \u00a0 \u00a0 Stream Open(string path, FileMode mode); \u00a0 \u00a0 void Delete(string path); }<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043c\u0435\u0442\u043e\u0434 \u0432 <strong>PhysicianFileProvider<\/strong><\/p>\n<pre><code class=\"cs\">public void Delete(string path) => File.Delete(path);<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043c\u0435\u0442\u043e\u0434 \u0432 <strong>MemoryFileProvider<\/strong><\/p>\n<pre><code class=\"cs\">public void Delete(string path) {}<\/code><\/pre>\n<p>\u0414\u043e\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u043d\u0430\u0448\u0438 \u0442\u0435\u0441\u0442\u044b, \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043e\u0431\u0435\u0440\u043d\u0435\u043c \u0432\u0435\u0441\u044c \u043a\u043e\u0434 \u0432 try finally, \u0447\u0442\u043e\u0431\u044b \u0443\u0434\u0430\u043b\u044f\u0442\u044c \u0444\u0430\u0439\u043b \u043f\u043e \u043e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044e \u0442\u0435\u0441\u0442\u0430. \u041f\u0440\u0438\u0432\u0435\u0434\u0443 \u043f\u0440\u0438\u043c\u0435\u0440 \u0442\u0435\u0441\u0442\u0430 \u043c\u0435\u0442\u043e\u0434\u0430 <strong>Save<\/strong>, \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0432\u043e \u0432\u0442\u043e\u0440\u043e\u043c \u0431\u0443\u0434\u0443\u0442 \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u044b:<\/p>\n<pre><code class=\"cs\">[Theory] [MemberData(nameof(TestData))] public void Save(string expect, List&lt;User> items) { \u00a0 \u00a0 \/\/ Arrange \u00a0 \u00a0 const string filePath = \"tmp.json\"; \u00a0 \u00a0 var fileProvider = new MemoryFileProvider();  \u00a0 \u00a0 try \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 var repository = new UserRepository(filePath, fileProvider);  \u00a0 \u00a0 \u00a0 \u00a0 \/\/ Act \u00a0 \u00a0 \u00a0 \u00a0 repository.Save(items); \u00a0 \u00a0 \u00a0 \u00a0 var json = fileProvider.ToJson(filePath); \u00a0   \u00a0 \u00a0 \u00a0 \u00a0 \/\/ Assert \u00a0 \u00a0 \u00a0 \u00a0 Assert.Equal(expect, json); \u00a0 \u00a0 } \u00a0 \u00a0 finally \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 try \u00a0 \u00a0 \u00a0 \u00a0 { \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \/\/ \u0423\u0434\u0430\u043b\u044f\u0435\u043c \u0444\u0430\u0439\u043b \u043f\u043e \u043e\u043a\u043e\u0447\u0430\u043d\u0438\u044e \u0442\u0435\u0441\u0442\u0430 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 fileProvider.Delete(filePath); \u00a0 \u00a0 \u00a0 \u00a0 } \u00a0 \u00a0 \u00a0 \u00a0 catch {} \u00a0 \u00a0 } }<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c <strong>new MemoryFileProvider()<\/strong> \u043d\u0430 <strong>new PhysicianFileProvider()<\/strong>, \u0442\u043e\u0433\u0434\u0430 \u0442\u0435\u0441\u0442 \u0431\u0443\u0434\u0435\u0442 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441 \u0444\u0430\u0439\u043b\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439. \u041f\u0440\u0438 \u0436\u0435\u043b\u0430\u043d\u0438\u0438 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u043d\u0443\u0436\u043d\u0443\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e <strong>IFileProvider<\/strong> \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430 \u0442\u0435\u0441\u0442\u0430.<\/p>\n<h2>\u0418\u0442\u043e\u0433<\/h2>\n<p>\u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043a\u043e\u0434\u0430 \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u043f\u043e\u043c\u043e\u0447\u044c \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u043a\u043b\u0430\u0441\u0441\u0430 \u0438 \u0443\u043b\u0443\u0447\u0448\u0438\u0442\u044c \u0434\u0438\u0437\u0430\u0439\u043d. \u041f\u043e\u0440\u043e\u0439 \u044d\u0442\u043e \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u043a \u0438\u0437\u0431\u044b\u0442\u043e\u0447\u043d\u043e\u0441\u0442\u0438, \u043d\u043e \u044d\u0442\u043e \u0441\u043b\u0438\u0445\u0432\u043e\u0439 \u043f\u043e\u043a\u0440\u044b\u0432\u0435\u0442\u0441\u044f \u043d\u0430\u0434\u0451\u0436\u043d\u043e\u0441\u0442\u044c\u044e \u043a\u043e\u0434\u0430.<\/p>\n<p>\u0411\u043b\u0430\u0433\u043e<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-339511","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/339511","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=339511"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/339511\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=339511"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=339511"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=339511"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}