AiDotNet
AiDotNet copied to clipboard
[Test Coverage] Implement Tests for Utility Helpers
Problem: Utility helpers have 0% test coverage. Files: SerializationHelper.cs, DeserializationHelper.cs, ConversionsHelper.cs, ParallelProcessingHelper.cs, TextProcessingHelper.cs, EnumHelper.cs. Goal: 80%+ coverage.
Issue #378: Junior Developer Implementation Guide
Unit Tests for Utility Helpers
Overview
Create comprehensive unit tests for utility helper classes in src/Helpers/ that provide cross-cutting functionality.
Files to Test:
SerializationHelper.cs- Serialization operations (558 lines)DeserializationHelper.cs- Deserialization operationsConversionsHelper.cs- Type conversionsParallelProcessingHelper.cs- Parallel execution utilitiesTextProcessingHelper.cs- Text and string operationsEnumHelper.cs- Enum utilities
Target: 0% → 80% test coverage
SerializationHelper Testing
Key Methods to Test
- SerializeNode() - Serialize decision tree nodes
- SerializeMatrix() - Serialize matrix to binary
- SerializeVector() - Serialize vector to binary
- SerializeTensor() - Serialize tensor to binary
- SerializeInterface() - Serialize interface instance
- WriteValue() - Write typed value to stream
Test Examples
[TestClass]
public class SerializationHelperTests
{
[TestMethod]
public void SerializeMatrix_ThenDeserialize_MatchesOriginal()
{
// Arrange
var original = new Matrix<double>(3, 2);
original[0, 0] = 1.0; original[0, 1] = 2.0;
original[1, 0] = 3.0; original[1, 1] = 4.0;
original[2, 0] = 5.0; original[2, 1] = 6.0;
// Act
byte[] serialized = SerializationHelper<double>.SerializeMatrix(original);
var deserialized = SerializationHelper<double>.DeserializeMatrix(serialized);
// Assert
Assert.AreEqual(3, deserialized.Rows);
Assert.AreEqual(2, deserialized.Columns);
for (int i = 0; i < 3; i++)
for (int j = 0; j < 2; j++)
Assert.AreEqual(original[i, j], deserialized[i, j]);
}
[TestMethod]
public void SerializeVector_PreservesAllValues()
{
// Arrange
var original = new Vector<double>(new[] { 1.0, 2.5, 3.7, -4.2 });
// Act
byte[] serialized = SerializationHelper<double>.SerializeVector(original);
var deserialized = SerializationHelper<double>.DeserializeVector(serialized);
// Assert
Assert.AreEqual(4, deserialized.Length);
for (int i = 0; i < 4; i++)
Assert.AreEqual(original[i], deserialized[i], 1e-10);
}
[TestMethod]
public void SerializeNode_WithChildren_PreservesTreeStructure()
{
// Arrange
var root = new DecisionTreeNode<double>
{
IsLeaf = false,
FeatureIndex = 0,
SplitValue = 5.0,
Left = new DecisionTreeNode<double> { IsLeaf = true, Prediction = 1.0 },
Right = new DecisionTreeNode<double> { IsLeaf = true, Prediction = 0.0 }
};
using var ms = new MemoryStream();
using var writer = new BinaryWriter(ms);
// Act
SerializationHelper<double>.SerializeNode(root, writer);
writer.Flush();
ms.Position = 0;
using var reader = new BinaryReader(ms);
var deserialized = SerializationHelper<double>.DeserializeNode(reader);
// Assert
Assert.IsNotNull(deserialized);
Assert.IsFalse(deserialized.IsLeaf);
Assert.AreEqual(0, deserialized.FeatureIndex);
Assert.AreEqual(5.0, deserialized.SplitValue);
Assert.IsTrue(deserialized.Left.IsLeaf);
Assert.AreEqual(1.0, deserialized.Left.Prediction);
}
[TestMethod]
public void WriteValue_ForDifferentTypes_SerializesCorrectly()
{
using var ms = new MemoryStream();
using var writer = new BinaryWriter(ms);
// Act
SerializationHelper<double>.WriteValue(writer, 3.14);
SerializationHelper<float>.WriteValue(writer, 2.71f);
SerializationHelper<int>.WriteValue(writer, 42);
writer.Flush();
ms.Position = 0;
using var reader = new BinaryReader(ms);
// Assert
Assert.AreEqual(3.14, SerializationHelper<double>.ReadValue(reader));
Assert.AreEqual(2.71f, SerializationHelper<float>.ReadValue(reader));
Assert.AreEqual(42, SerializationHelper<int>.ReadValue(reader));
}
}
Test Checklist
- [ ] Matrix serialization/deserialization
- [ ] Vector serialization/deserialization
- [ ] Tensor serialization/deserialization
- [ ] Decision tree node serialization
- [ ] All numeric types (double, float, int, etc.)
- [ ] Null handling
- [ ] Empty collections
- [ ] Large data structures
DeserializationHelper Testing
Test Pattern
Deserialization tests mirror serialization - the key is round-trip testing:
[TestMethod]
public void RoundTrip_Matrix_PreservesData()
{
var original = CreateTestMatrix();
var serialized = SerializationHelper<double>.SerializeMatrix(original);
var deserialized = DeserializationHelper<double>.DeserializeMatrix(serialized);
AssertMatricesEqual(original, deserialized);
}
ConversionsHelper Testing
Key Methods to Test
- ToDouble() - Convert T to double
- ToFloat() - Convert T to float
- ToInt() - Convert T to int
- FromDouble() - Convert double to T
- ConvertArray() - Convert array types
- ConvertMatrix() - Convert matrix types
- ConvertVector() - Convert vector types
Test Examples
[TestClass]
public class ConversionsHelperTests
{
[TestMethod]
public void ToDouble_FromFloat_ConvertsCorrectly()
{
// Arrange
float value = 3.14f;
// Act
double result = ConversionsHelper.ToDouble(value);
// Assert
Assert.AreEqual(3.14, result, 1e-6);
}
[TestMethod]
public void ConvertMatrix_FromIntToDouble_PreservesValues()
{
// Arrange
var intMatrix = new Matrix<int>(2, 2);
intMatrix[0, 0] = 1; intMatrix[0, 1] = 2;
intMatrix[1, 0] = 3; intMatrix[1, 1] = 4;
// Act
var doubleMatrix = ConversionsHelper.ConvertMatrix<int, double>(intMatrix);
// Assert
Assert.AreEqual(1.0, doubleMatrix[0, 0]);
Assert.AreEqual(4.0, doubleMatrix[1, 1]);
}
[TestMethod]
public void ConvertVector_HandlesLargePrecisionLoss()
{
// Arrange
var doubleVector = new Vector<double>(new[] { 1.99999, 2.00001 });
// Act
var intVector = ConversionsHelper.ConvertVector<double, int>(doubleVector);
// Assert
Assert.AreEqual(1, intVector[0]); // Truncated
Assert.AreEqual(2, intVector[1]); // Truncated
}
}
Test Checklist
- [ ] All numeric type conversions
- [ ] Array conversions
- [ ] Matrix conversions
- [ ] Vector conversions
- [ ] Precision loss handling
- [ ] Overflow handling
- [ ] Null handling
ParallelProcessingHelper Testing
Key Methods to Test
- ParallelFor() - Parallel loop execution
- ParallelForEach() - Parallel collection iteration
- ParallelMap() - Map operation in parallel
- ParallelReduce() - Reduce operation in parallel
- GetOptimalThreadCount() - Determine thread count
- CreatePartitions() - Partition data for parallel processing
Test Examples
[TestClass]
public class ParallelProcessingHelperTests
{
[TestMethod]
public void ParallelFor_ProcessesAllElements()
{
// Arrange
var results = new int[100];
// Act
ParallelProcessingHelper.ParallelFor(0, 100, i =>
{
results[i] = i * 2;
});
// Assert
for (int i = 0; i < 100; i++)
Assert.AreEqual(i * 2, results[i]);
}
[TestMethod]
public void ParallelMap_TransformsAllElements()
{
// Arrange
var input = Enumerable.Range(0, 100).ToList();
// Act
var output = ParallelProcessingHelper.ParallelMap(
input, x => x * x);
// Assert
for (int i = 0; i < 100; i++)
Assert.AreEqual(i * i, output[i]);
}
[TestMethod]
public void ParallelReduce_ComputesSum()
{
// Arrange
var data = Enumerable.Range(1, 100).ToList();
// Act
int sum = ParallelProcessingHelper.ParallelReduce(
data, (a, b) => a + b, identity: 0);
// Assert
Assert.AreEqual(5050, sum); // Sum of 1..100 = 5050
}
[TestMethod]
public void GetOptimalThreadCount_ReturnsReasonableValue()
{
// Act
int threadCount = ParallelProcessingHelper.GetOptimalThreadCount();
// Assert
Assert.IsTrue(threadCount > 0);
Assert.IsTrue(threadCount <= Environment.ProcessorCount);
}
[TestMethod]
public void CreatePartitions_DividesDataEvenly()
{
// Arrange
var data = Enumerable.Range(0, 100).ToList();
int partitionCount = 4;
// Act
var partitions = ParallelProcessingHelper.CreatePartitions(
data, partitionCount);
// Assert
Assert.AreEqual(4, partitions.Count);
Assert.AreEqual(100, partitions.Sum(p => p.Count));
Assert.IsTrue(partitions.All(p => p.Count >= 24 && p.Count <= 26),
"Partitions should be roughly equal");
}
}
Test Checklist
- [ ] Parallel loops complete all iterations
- [ ] Thread safety (no race conditions)
- [ ] Correct results vs sequential
- [ ] Performance improvement (optional)
- [ ] Exception handling
- [ ] Partitioning algorithms
- [ ] Thread count optimization
TextProcessingHelper Testing
Key Methods to Test
- Tokenize() - Split text into tokens
- RemoveStopWords() - Filter common words
- Stem() - Reduce words to stems
- Lemmatize() - Reduce words to lemmas
- CalculateTFIDF() - Term frequency-inverse document frequency
- NormalizeText() - Clean and normalize text
- ExtractNGrams() - Extract n-grams
Test Examples
[TestClass]
public class TextProcessingHelperTests
{
[TestMethod]
public void Tokenize_SplitsOnWhitespace()
{
// Arrange
string text = "The quick brown fox";
// Act
var tokens = TextProcessingHelper.Tokenize(text);
// Assert
Assert.AreEqual(4, tokens.Count);
Assert.AreEqual("The", tokens[0]);
Assert.AreEqual("fox", tokens[3]);
}
[TestMethod]
public void RemoveStopWords_FiltersCommonWords()
{
// Arrange
var tokens = new List<string> { "the", "cat", "is", "on", "mat" };
// Act
var filtered = TextProcessingHelper.RemoveStopWords(tokens);
// Assert
Assert.AreEqual(2, filtered.Count);
Assert.IsTrue(filtered.Contains("cat"));
Assert.IsTrue(filtered.Contains("mat"));
Assert.IsFalse(filtered.Contains("the"));
Assert.IsFalse(filtered.Contains("is"));
}
[TestMethod]
public void ExtractNGrams_GeneratesBigrams()
{
// Arrange
var tokens = new List<string> { "the", "quick", "brown", "fox" };
// Act
var bigrams = TextProcessingHelper.ExtractNGrams(tokens, n: 2);
// Assert
Assert.AreEqual(3, bigrams.Count);
Assert.IsTrue(bigrams.Contains("the quick"));
Assert.IsTrue(bigrams.Contains("quick brown"));
Assert.IsTrue(bigrams.Contains("brown fox"));
}
[TestMethod]
public void NormalizeText_ConvertsToLowerAndRemovesPunctuation()
{
// Arrange
string text = "Hello, World! This is a TEST.";
// Act
string normalized = TextProcessingHelper.NormalizeText(text);
// Assert
Assert.AreEqual("hello world this is a test", normalized);
}
}
Test Checklist
- [ ] Tokenization (various delimiters)
- [ ] Stop word removal
- [ ] Stemming algorithms
- [ ] Lemmatization
- [ ] TF-IDF calculation
- [ ] Text normalization
- [ ] N-gram extraction
- [ ] Unicode handling
- [ ] Edge cases (empty, punctuation-only)
EnumHelper Testing
Key Methods to Test
- Parse() - Parse string to enum
- TryParse() - Safe parse with output
- GetValues() - Get all enum values
- GetNames() - Get all enum names
- GetDescription() - Get description attribute
- ToInt() - Convert enum to int
- FromInt() - Convert int to enum
Test Examples
[TestClass]
public class EnumHelperTests
{
public enum TestEnum
{
[Description("First Value")]
First = 1,
[Description("Second Value")]
Second = 2,
[Description("Third Value")]
Third = 3
}
[TestMethod]
public void Parse_ValidString_ReturnsEnum()
{
// Act
var result = EnumHelper.Parse<TestEnum>("Second");
// Assert
Assert.AreEqual(TestEnum.Second, result);
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void Parse_InvalidString_ThrowsException()
{
// Act
EnumHelper.Parse<TestEnum>("Invalid");
}
[TestMethod]
public void TryParse_ValidString_ReturnsTrue()
{
// Act
bool success = EnumHelper.TryParse<TestEnum>("Third", out var result);
// Assert
Assert.IsTrue(success);
Assert.AreEqual(TestEnum.Third, result);
}
[TestMethod]
public void GetValues_ReturnsAllEnumValues()
{
// Act
var values = EnumHelper.GetValues<TestEnum>();
// Assert
Assert.AreEqual(3, values.Count());
Assert.IsTrue(values.Contains(TestEnum.First));
Assert.IsTrue(values.Contains(TestEnum.Third));
}
[TestMethod]
public void GetDescription_ReturnsAttribute()
{
// Act
string description = EnumHelper.GetDescription(TestEnum.Second);
// Assert
Assert.AreEqual("Second Value", description);
}
[TestMethod]
public void ToInt_ConvertsCorrectly()
{
// Act
int value = EnumHelper.ToInt(TestEnum.Second);
// Assert
Assert.AreEqual(2, value);
}
[TestMethod]
public void FromInt_ConvertsCorrectly()
{
// Act
var result = EnumHelper.FromInt<TestEnum>(3);
// Assert
Assert.AreEqual(TestEnum.Third, result);
}
}
Test Checklist
- [ ] Parse valid strings
- [ ] Parse invalid strings (exception)
- [ ] TryParse with valid/invalid strings
- [ ] Get all values
- [ ] Get all names
- [ ] Get descriptions from attributes
- [ ] Int conversions (to/from)
- [ ] Case-insensitive parsing
- [ ] Edge cases (null, empty string)
Test File Structure
tests/Helpers/
├── SerializationHelperTests.cs
├── DeserializationHelperTests.cs
├── ConversionsHelperTests.cs
├── ParallelProcessingHelperTests.cs
├── TextProcessingHelperTests.cs
└── EnumHelperTests.cs
Success Criteria
Definition of Done
- [ ] 6 test files created
- [ ] 90+ tests total (15 per helper minimum)
- [ ] All tests passing
- [ ] Code coverage >= 75% for each helper
- [ ] Round-trip serialization tested
- [ ] All type conversions tested
- [ ] Parallel operations verified
Quality Checklist
- [ ] Serialization round-trip for all types
- [ ] Type conversion edge cases (overflow, precision loss)
- [ ] Parallel processing thread safety
- [ ] Text processing handles Unicode
- [ ] Enum helper handles all enum types
- [ ] Null/empty input handling
Resources
- See ISSUE_349_JUNIOR_DEV_GUIDE.md for helper testing patterns
- Serialization reference: BinaryReader/BinaryWriter documentation
- Parallel patterns: Task Parallel Library (TPL) documentation
Target: Create tests ensuring utility helpers provide reliable cross-cutting functionality for the entire library.