如何确定一个 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 执行操作,所以 Employee
和 WorkYear
形成一个内聚的实体,其中两个 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)
- 你注入行为(服务)
我正在从事一个在设计时并未考虑单元测试的项目。
因为在 StartWorking()
方法内部我创建了一个 WorkYear
的新实例并调用 year.RecalculateAllTime()
,WorkYear
class 被认为是外部依赖(在单元测试方面)?
或者由于 Employee
通过组合关系绑定到 WorkYear
+ Employee
是为了对 WorkYears 执行操作,所以 Employee
和 WorkYear
形成一个内聚的实体,其中两个 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)
- 你注入行为(服务)