TestValueFactory C# & Mapper Tests

When you’re writing tests, especially mapper tests, knowing the value which is mapped is not of great use. What you’re testing is that the value is correctly mapped, or bound to the accurate location in destination.

Mapping is very often not considered by young developer (and sometimes senior developer) as a source of error. In my opinion, actually it is. Thus, that’s why I am sharing those code snippets to help people writing tests on mappers.

Keep this sentence in mind, when writing tests in group code project : “Backup your a** !”. Everything can be a source of error.

The TestValueHelper implementation provide issue resolution to handle random type value generation. This class is certainly to be developped.

using System;
using System.Collections.Generic;

namespace maximilienzakowski.com.TestToolKit
{
    public static class TestValueFactory
    {
        private static readonly Random _random = new Random();

        public static int GetRandomIntValue()
        {
            return _random.Next();
        }

        public static int GetRandomIntValue(int minValue, int maxValue)
        {
            return _random.Next(minValue, maxValue);
        }

        public static int GetRandomIntValue(int[] except)
        {
            var exceptSet = new HashSet<int>(except??new int[] { });
            int result;
            do
            {
                result = _random.Next();
            }
            while (exceptSet.Contains(result));
            return result;
        }

        public static DateTime GetRandomDateTimeValue()
        {
            DateTime start = new DateTime(1970, 1, 1);
            int range = (DateTime.Today - start).Days;
            return start.AddDays(_random.Next(range))
                .AddHours(_random.Next(0, 24))
                .AddMinutes(_random.Next(0, 60))
                .AddSeconds(_random.Next(0, 60));
        }

        public static double GetRandomDoubleValue()
        {
            return _random.Next() * _random.NextDouble();
        }

        public static decimal GetRandomDecimalValue()
        {
            double RandH, RandL;
            do
            {
                RandH = _random.NextDouble();
                RandL = _random.NextDouble();
            } while ((RandH > 0.99999999999999d) || (RandL > 0.99999999999999d));
            return (decimal)RandH + (decimal)RandL / 1E14m;
        }
        public static decimal GetRandomDecimalValue( decimal minValue, decimal maxValue)
        {
            return GetRandomDecimalValue() * (maxValue - minValue) + minValue;
        }

        public static T GetRandomEnumValue<T>() where T : System.Enum
        {
            var values = Enum.GetValues(typeof(T));
            return (T)values.GetValue(_random.Next(0,values.Length-1));
        }

        public static string GetRandomStringValue()
        {
            return GetRandomStringValue(20);
        }

        public static string GetRandomStringValue(int length)
        {
            const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
            return new string(Enumerable.Repeat(chars, length).Select(s => s[_random.Next(s.Length)]).ToArray());
        }
    }
}

Tested code snippet which is a builder + mapper association :

public class ItemToInvoiceRowConverter2
    {
        public IInvoiceRow Convert(IItem converted)
        {
            if (converted == null) return null;
            var result = new InvoiceRow();
            //result.Number = converted.Id;
            result.IdItem = converted.Id;
            result.Kind = converted.Kind;
            result.Label = converted.Name;
            //result.Vat = converted.;
            result.Unit = converted.Unit;
            result.UnitPrice = converted.Price;
            //result.Discount = converted.Id;
            //result.Quantity = converted.Id;
            return result;
        }
    }

Unit test code :

using myApplication.Converters;
using myApplication.Models;
using maximilienzakowski.com.TestToolKit;
using Moq;
using NUnit.Framework;

namespace myApplication.Tests
{
    [TestFixture]
    public class ItemToInvoiceRowConverterTests
    {
        private ItemToInvoiceRowConverter2 BuildTested()
        {
            return new ItemToInvoiceRowConverter2();
        }

        [Test]
        public void ConvertShouldReturnNullWhenConvertedIsNull()
        {
            var tested = BuildTested();
            var result = tested.Convert(null);
            Assert.IsNull(result);
        }


        [Test]
        public void ConvertShouldConvertAndMapAccurately()
        {
            var converted = new Mock<IItem>();
            var id = TestValueFactory.GetRandomIntValue();
            var kind = TestValueFactory.GetRandomStringValue();
            var name = TestValueFactory.GetRandomStringValue();
            var unit = TestValueFactory.GetRandomStringValue();
            var price = TestValueFactory.GetRandomDecimalValue();

            converted.SetupGet(x => x.Id).Returns(id);
            converted.SetupGet(x => x.Kind).Returns(kind);
            converted.SetupGet(x => x.Name).Returns(name);
            converted.SetupGet(x => x.Unit).Returns(unit);
            converted.SetupGet(x => x.Price).Returns(price);
            var tested = BuildTested();
            var result = tested.Convert(converted.Object);
            Assert.IsNotNull(result);
            Assert.AreEqual(id, result.IdItem);
            Assert.AreEqual(kind, result.Kind);
            Assert.AreEqual(name, result.Label);
            Assert.AreEqual(unit, result.Unit);
            Assert.AreEqual(price, result.UnitPrice);

            converted.VerifyGet(x => x.Id,Times.Once);
            converted.VerifyGet(x => x.Kind, Times.Once);
            converted.VerifyGet(x => x.Name, Times.Once);
            converted.VerifyGet(x => x.Unit, Times.Once);
            converted.VerifyGet(x => x.Price, Times.Once);
            converted.VerifyNoOtherCalls();
        }
    }
}

Hope this will help someone else than me…