0Pricing
C# Academy · Урок

Заглушки через интерфейсы и построители тестовых данных

Внедряйте зависимости через интерфейсы, создавайте простые заглушки для наблюдения за поведением и формируйте сложные входные данные с помощью небольшого построителя тестовых данных.

«Заглушки через интерфейсы и построители тестовых данных» — бесплатный урок C# Academy на CoddyKit. Это урок 2 из 3. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C# Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C# Academy содержит 3 уроков всего.

Изоляция с помощью подмен

Цель: изолировать проверяемый код.

  • Зависите от интерфейсов
  • Предоставляйте подмены в тестах
  • Используйте небольшой построитель для сложных входных данных
  • Наблюдайте за вызовами, а не за реальным миром

Внедрение времени через IClock

Скройте время за IClock. Тесты будут передавать подмену часов для получения предсказуемых результатов.

using System;

public interface IClock { DateTime NowUtc(); }

public sealed class SystemClock : IClock
{
  public DateTime NowUtc() { return DateTime.UtcNow; }
}

public sealed class DiscountService
{
  private readonly IClock _clock;
  public DiscountService(IClock clock) { _clock = clock; }

  // 50% off on Black Friday (Nov 27), else 0
  public int GetDiscountPercent()
  {
    DateTime d = _clock.NowUtc().Date;
    if (d.Month == 11 && d.Day == 27) return 50;
    return 0;
  }
}

public class Program
{
  public static void Main(string[] args)
  {
    DiscountService live = new DiscountService(new SystemClock());
    Console.WriteLine("Today's discount: " + live.GetDiscountPercent() + "%");
  }
}

Реализация подмены в тестах

Подмена реализует интерфейс и возвращает управляемые данные, поэтому проверки остаются стабильными.

using System;

public interface IClock { DateTime NowUtc(); }

public sealed class FakeClock : IClock
{
  private readonly DateTime _fixed;
  public FakeClock(DateTime fixedUtc) { _fixed = fixedUtc; }
  public DateTime NowUtc() { return _fixed; }
}

public static class Assert
{
  public static void AreEqual(int expected, int actual)
  {
    if (expected != actual)
      throw new Exception("Expected " + expected + " but got " + actual);
  }
}

public sealed class DiscountService
{
  private readonly IClock _clock;
  public DiscountService(IClock clock) { _clock = clock; }

  public int GetDiscountPercent()
  {
    DateTime d = _clock.NowUtc().Date;
    if (d.Month == 11 && d.Day == 27) return 50;
    return 0;
  }
}

public class Program
{
  public static void Main(string[] args)
  {
    try
    {
      // Black Friday: 2015-11-27
      DiscountService s1 = new DiscountService(new FakeClock(new DateTime(2015, 11, 27, 0, 0, 0, DateTimeKind.Utc)));
      Assert.AreEqual(50, s1.GetDiscountPercent());

      // Normal day
      DiscountService s2 = new DiscountService(new FakeClock(new DateTime(2015, 11, 26, 0, 0, 0, DateTimeKind.Utc)));
      Assert.AreEqual(0, s2.GetDiscountPercent());

      Console.WriteLine("PASS: discount logic with fake clock");
    }
    catch (Exception ex)
    {
      Console.WriteLine("FAIL: " + ex.Message);
    }
  }
}

Наблюдение за побочными эффектами

Подмена может фиксировать вызовы, чтобы тест проверял побочные эффекты без настоящей отправки электронной почты или SMS.

using System;
using System.Collections.Generic;

public interface INotifier { void Send(string message); }

public sealed class OrderService
{
  private readonly INotifier _notifier;
  public OrderService(INotifier notifier) { _notifier = notifier; }

  public void Place(string product, int qty)
  {
    if (qty <= 0) throw new ArgumentOutOfRangeException("qty");
    // ...
    _notifier.Send("Placed: " + product + " x" + qty);
  }
}

public sealed class FakeNotifier : INotifier
{
  public readonly List<string> Messages = new List<string>();
  public void Send(string message) { Messages.Add(message); }
}

public static class Assert
{
  public static void IsTrue(bool condition, string message)
  {
    if (!condition) throw new Exception(message);
  }
}

public class Program
{
  public static void Main(string[] args)
  {
    FakeNotifier fake = new FakeNotifier();
    OrderService svc = new OrderService(fake);

    try
    {
      svc.Place("Book", 2);
      Assert.IsTrue(fake.Messages.Count == 1, "Expected exactly one notification");
      Assert.IsTrue(fake.Messages[0].IndexOf("Book") >= 0, "Message should mention product");
      Console.WriteLine("PASS: observed notification via fake");
    }
    catch (Exception ex)
    {
      Console.WriteLine("FAIL: " + ex.Message);
    }
  }
}

Построитель тестовых данных

Небольшой построитель задаёт значения по умолчанию и переопределяет только нужные тесту параметры, делая подготовку короткой и понятной.

using System;

public sealed class Order
{
  public string Product;
  public int Quantity;
  public double UnitPrice;

  public double Total() { return Quantity * UnitPrice; }
}

// Minimal builder for tests
public sealed class OrderBuilder
{
  private string _product = "Item";
  private int _qty = 1;
  private double _price = 1.0;

  public OrderBuilder WithProduct(string name) { _product = name; return this; }
  public OrderBuilder WithQty(int qty) { _qty = qty; return this; }
  public OrderBuilder WithPrice(double price) { _price = price; return this; }
  public Order Build()
  {
    Order o = new Order();
    o.Product = _product;
    o.Quantity = _qty;
    o.UnitPrice = _price;
    return o;
  }
}

public static class Assert
{
  public static void AreEqual(double expected, double actual)
  {
    if (Math.Abs(expected - actual) > 0.0001)
      throw new Exception("Expected " + expected + " but got " + actual);
  }
}

public class Program
{
  public static void Main(string[] args)
  {
    try
    {
      // Quick setup with readable intent
      Order o = new OrderBuilder()
        .WithProduct("Pen")
        .WithQty(3)
        .WithPrice(2.5)
        .Build();

      Assert.AreEqual(7.5, o.Total());
      Console.WriteLine("PASS: builder created valid order");
    }
    catch (Exception ex)
    {
      Console.WriteLine("FAIL: " + ex.Message);
    }
  }
}

Практические советы

Советы:

  • Для изоляции побочных эффектов отдавайте предпочтение небольшим интерфейсам
  • Делайте подмены небольшими; сохраняйте результаты в полях или списках
  • Построители хранят разумные значения по умолчанию и используют цепочные методы установки
  • Тесты читаются как истории: подготовка → действие → проверка

Подход к изоляции

Быстрая проверка: как проще всего изолировать зависимость в модульном тесте?

Итоги

Итоги: внедряйте интерфейсы, используйте подмены для управления поведением и его наблюдения, а входные данные быстро создавайте с помощью небольшого построителя тестовых данных.

Часто задаваемые вопросы

Урок «Заглушки через интерфейсы и построители тестовых данных» бесплатный?

Да — полный текст урока «Заглушки через интерфейсы и построители тестовых данных» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C# Academy, подпишись на CoddyKit PRO. Курс C# Academy содержит 3 уроков всего.

Чему я научусь в уроке «Заглушки через интерфейсы и построители тестовых данных»?

Внедряйте зависимости через интерфейсы, создавайте простые заглушки для наблюдения за поведением и формируйте сложные входные данные с помощью небольшого построителя тестовых данных. Ты практикуешь C# Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать C# Academy?

Предыдущий опыт не требуется. C# Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 3.

Сколько времени занимает урок «Заглушки через интерфейсы и построители тестовых данных»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке C# Academy?

Да. Каждый урок C# Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Настройка xUnit/MSTest и утверждения
  2. Заглушки через интерфейсы и построители тестовых данных
  3. Отладка неудачных тестов
← Назад к C# Academy