如何确定一个 class 是一个内聚部分还是另一个 class 的依赖(在单元测试方面)?

How to decide whether one class is a cohesive part vs dependency of another class (In terms of unit testing)?

我正在从事一个在设计时并未考虑单元测试的项目。

因为在 StartWorking() 方法内部我创建了一个 WorkYear 的新实例并调用 year.RecalculateAllTime()WorkYear class 被认为是外部依赖(在单元测试方面)?

或者由于 Employee 通过组合关系绑定到 WorkYear + Employee 是为了对 WorkYears 执行操作,所以 EmployeeWorkYear形成一个内聚的实体,其中两个 class 不被视为彼此的依赖关系?

换句话说,StartWorking(...) 方法应该与 WorkYear class 分开测试吗?

public abstract class Employee
{
    private List<WorkYear> _workYears;
    private readonly IntervalCalculator _intervalCalculator;
    // Other fields...

    protected Employee(IntervalCalculator intervalCalculator)
    {
        _intervalCalculator = intervalCalculator;
        WorkYears = new List<WorkYear>();
    }

    public IEnumerable<WorkYear> WorkYears
    {
        get => _workYears.AsReadOnly();
        private set => _workYears = value.ToList();
    }
    // Other properties...
    

    public void StartWorking(DateTime joinedCompany)
    {
        List<PayPeriodInterval> allIntervals = _intervalCalculator.GenerateIntervalsFor(joinedCompany.Date.Year);

        PayPeriodInterval currentInterval = allIntervals.Find(i => i.StartDate <= joinedCompany && joinedCompany <= i.EndDate);
        
        PayPeriod firstPeriod = CalculateFirstPeriod(joinedCompany, currentInterval);

        // There is a possibility that employee worked during this year and returned during
        // the same exact year or even month. That is why we are trying to find this year in database:
        WorkYear year = WorkYears.FirstOrDefault(y => y.CurrentYear == joinedCompany.Year);

        if (year == null)
        {
            // Create new year with current and future periods.
            year = new WorkYear(joinedCompany.Year, this, new List<PayPeriod> {firstPeriod});

            AddYear(year);
        }
        else
        {
            // There is a possibility that employee left and got back during the same period.
            // That is why we should try to find this period so that we don't override it with new one:
            PayPeriod existingPeriod = year.GetPeriodByDate(joinedCompany);

            if (existingPeriod != null)
            {
                var oldCurrentPeriodWorktime = new TimeSpan(existingPeriod.WorktimeHours, existingPeriod.WorktimeMinutes, 0);

                firstPeriod = CalculateFirstPeriod(joinedCompany, currentInterval, oldCurrentPeriodWorktime);
            }

            year.PayPeriods.Add(firstPeriod);
        }

        List<PayPeriodInterval> futureIntervals = allIntervals.FindAll(i => currentInterval.EndDate < i.StartDate);

        List<PayPeriod> futurePeriods = NewPeriods(futureIntervals);

        year.PayPeriods.AddRange(futurePeriods);

        year.RecalculateAllTime();
    }

    public abstract List<PayPeriod> NewPeriods(List<PayPeriodInterval> intervals);

    public void AddYear(WorkYear workYear) => _workYears.Add(workYear);

    protected abstract PayPeriod CalculateFirstPeriod(DateTime firstDayAtWork, PayPeriodInterval firstPeriodInerval, TimeSpan initialTime = default);

    // Other methods...

}

public class WorkYear
{
    public WorkYear(int currentYear, Employee employee, List<PayPeriod> periods)
    {
        Employee = employee;
        EmployeeId = employee.Id;
        CurrentYear = currentYear;
        PayPeriods = periods ?? new List<PayPeriod>();

        foreach (PayPeriod period in PayPeriods)
        {
            period.WorkYear = this;
            period.WorkYearId = Id;
        }
    }

    public int EmployeeId { get; }
    public int CurrentYear { get; }
    public Employee Employee { get; }
    public List<PayPeriod> PayPeriods { get; set; }
    
    // Other roperties...


    public void RecalculateAllTime()
    {
        //Implementation Logic
    }
}

超级大警告:这些东西很快就会变得固执己见。有很多有效的设计!

好了,说完了。 DTO(数据传输对象)不是外部依赖项。你不注射它们。

WorkYear 具有 DTO 的所有特征(除了该方法)。我认为你现在还可以。因为它有 RecalculateAllTime 方法,所以它应该 进行单元测试。在这种情况下,外部依赖的一个例子是 获取 工作年份列表。

基本的经验法则是:

  • 您编写数据 (DTO)
  • 你注入行为(服务)