This is an example of a typical function from a project I used to work on. I have troubling grasping unit testing because if I want to test this function properly, I would have to write about 50-100 unit tests to cover most of the possible code paths to ensure all the logic works correctly, and this is just a very small part of the application.
Almost every function with business logic is like this, and 90% of the codepaths for each function are critical, which means that if the codepath has a bug then the entire function is broken. While it's reasonable to refactor this function, the algorithm must be the same after refactoring, and that means you can't avoid writing the same amount of tests after refactoring, the only thing refactoring would change is that some of the test would be integration tests rather than unit tests. How do real world projects thoroughly test everything with automated without spending so much time writing tests to the point you would take more time writing tests than manually testing everything? Is there anything I'm missing?
private async Task ValidateItem(Item item, IEnumerable<Item2> items2,
ValidatorCache cache, CancellationToken cancellation)
{
(bool prop1IsValid, var propData) = await cache.ValidateProperty(item.Property1, cancellation);
if (propData != null)
{
item.Property1 = propData.NAME;
}
Item2 Item2 = null;
if (!string.IsNullOrWhiteSpace(item.Item2Id))
{
Item2 = items2.FirstOrDefault(a => a.Id == item.Item2Id && a.Code == item.Code);
}
PropData correctPropData = null, item2PropData = null;
if (Item2 == null)
{
item.ItemState = ItemState.DoesNotExistInItems2;
if (prop1IsValid)
{
FillFields(propData);
correctPropData = propData;
}
}
else
{
bool prop2Valid = false;
item.Property2 = Item2.NAME?.ToUpperInvariant();
if (string.IsNullOrWhiteSpace(item.Property3))
{
item.Property3 = Item2.Property3;
}
if (string.IsNullOrWhiteSpace(item.Property4))
{
item.Property4 = Item2.Property4;
}
if (string.IsNullOrWhiteSpace(item.Property5))
{
item.Property5 = Item2.Property5;
}
if (string.IsNullOrWhiteSpace(item.Property6))
{
item.Property6 = Item2.Property6;
}
(prop2Valid, item2PropData) = await cache.ValidateProperty(item2.Property2, cancellation);
if (prop1IsValid && prop2Valid)
{
item.ItemState = ItemState.OK;
item.ChooseProp = ChooseProp.Prop1;
correctPropData = propData;
}
else
{
if (prop1IsValid)
{
item.ItemState = ItemState.OK;
item.ChooseProp = ChooseProp.Prop1;
correctPropData = propData;
}
else if (prop2Valid)
{
item.ItemState = ItemState.OK;
item.ChooseProp = ChooseProp.Prop2;
correctPropData = item2PropData;
item.Property1 = item.Property2;
}
else
{
item.ItemState = ItemState.Property1Error;
return;
}
}
FillFields(correctPropData);
}
if (Item2 == null && item.ItemState != ItemState.Property1Error && string.IsNullOrWhiteSpace(item.Property7) && item.Flag != true)
{
string prop = item.Item == ChooseProp.Prop2 ? item.Property2 : item.Property1;
var validator = new Prop7Validator(await cache.GetProp7(prop, cancellation));
item.Property7 = validator.GenerateProp7(item);
}
else
{
if (item.ItemState != ItemState.Property1Error && correctPropData != null)
{
string prop7 = item2 != null ? item2.Property7 : item.Property7;
var validator = new Prop7Validator(await cache.GetProp7(correctPropData.NAME, cancellation));
item.Property7 = validator.GetMissingData(prop7, item);
}
}
if (correctPropData != null)
{
var prop7Data = await cache.GetProp7(correctPropData.NAME, cancellation);
var validator = new Prop7Validator(prop7Data);
if (!validator.ValidateProp7(item.Property7))
{
item.ItemState = ItemState.Property7Error;
}
else if (string.IsNullOrWhiteSpace(item.Property3))
{
item.ItemState = ItemState.MissingProperty3;
}
else if (item.Property8 != "2" && item.Property9 <= 0)
{
item.ItemState = ItemState.InvalidProperty9;
}
else
{
item.ItemState = ItemState.OK;
}
}
void FillFields(PropData propData)
{
if (item.Property3 == "01" && item.Property9 > 0)
{
item.Property10 = item.Property9;
}
item.Property11 = propData.m012;
if (item.Property11 == "2")
{
if (item.Property3 == "2" && item.Property9 > 0)
{
item.Property12 = item.Property9;
item.Property10 = item.Property9;
}
else if (item.Property10 > 0)
{
item.Property12 = item.Property10;
}
else
{
item.Property10 = item.Property12;
}
}
else if (item.Property3 == item.Property11)
{
item.Property12 = item.Property9;
}
}
}