piątek, 25 października 2013

Testowanie po IEquatable

Tak się zastanawiam nad testowaniem obiektów, które można przyrównać do obiektów tego samego typu (metoda Equals). Przypuśćmy, że masz klasę, która implementuję IEquatable<WrappedString>.
public class WrappedString : IEquatable<WrappedString>
{
        public string StringValue { get; set; }
 
        public WrappedString(string stringValue)
        {
            StringValue = stringValue;
        }
 
        public bool Equals(WrappedString other)
        {
            return this.StringValue == other.StringValue;
        }
}
Każda klasa implementująca IEquatable<WrappedString> powinna spełniać parę standardów, m.in. jeżeli T jest strukturą to domyślna wartość powinna zwrócić tą samą wartość, a kiedy obiekt klasy porównujemy do null zawsze powinna zwracać fałsz.
 public abstract class EquatableRelation<TType> 
        where TType : IEquatable<TType>
{
        public abstract TType GetItemX();  //object to test
        private bool IsValueType()
        {
            return typeof (TType).IsValueType;
        }

  #region Default values
        protected virtual void It_should_return_false_because_comparing_to_null()
        {
            if (!IsValueType())
            {
                TType number1 = GetItemX();
                Assert.IsFalse(number1.Equals(null));
            }
        }
 
        protected virtual void It_should_return_true_for_default_value_in_valueType()
        {
            if (IsValueType())
            {
                var value1 = default(TType);
                var value2 = default(TType);
                Assert.IsTrue(value1.Equals(value2));
            }
        }
        #endregion
}

Obiekt powinien spełniać warunki relacji równoważności.
 public abstract class EquatableRelation<TType> 
        where TType : IEquatable<TType>
{
public abstract TType GetItemX();
public abstract TType GetItemY();
public abstract TType GetItemZ();

#region relation
//relacja zwrotna
protected virtual void It_should_be_reflexive_relation()
{
            TType number1 = GetItemX();
            if (!IsValueType())
            {
                Assert.IsNotNull(number1);
            }
            Assert.IsTrue(number1.Equals(number1));
}

 //relacja symetryczna
protected virtual void It_should_be_symmetric_relation()
{
            TType number1 = GetItemX();
            TType number2 = GetItemY();
 
            bool number1To2 = number1.Equals(number2);
            bool number2To1 = number2.Equals(number1);
            Assert.AreEqual(number1To2, number2To1);
}

//relacja przechodnia
protected virtual void It_should_be_Transitive_relation()
{
            TType number1 = GetItemX();
            TType number2 = GetItemY();
            TType number3 = GetItemZ();
 
            Assert.IsTrue(number1.Equals(number2));
            Assert.IsTrue(number2.Equals(number3));
            Assert.IsTrue(number1.Equals(number3));
 
}
#endregion
}
Oprócz tego zawsze powinno się zwracać taką samą wartość porównania:
 public abstract class EquatableRelation<TType> 
        where TType : IEquatable<TType>
{
private const int NumberOfRepeatedTestRuns = 10;

protected virtual void It_should_return_always_the_same_value_for_Equals()
{
            var bools= new List<bool>();
            for (int i = 0; i < NumberOfRepeatedTestRuns; i++)
            {
                TType number1 = GetItemX();
                TType number2 = GetItemY();
                bools.Add(number1.Equals(number2));
            }
            Assert.IsTrue(bools.All(x => x));
}
 
protected virtual void It_should_return_always_the_same_value_for_reflexive()
{
            var bools= new List<bool>();
            for (int i = 0; i < NumberOfRepeatedTestRuns; i++)
            {
                TType number1 = GetItemX();
                TType number2 = GetItemX();
                bools.Add(number1.Equals(number2));
            }
            Assert.IsTrue(bools.All(x => x));
}

protected virtual void It_should_return_always_true_for_default_values_in_valueType()
{
            if (IsValueType())
            {
                for (int i = 0; i < NumberOfRepeatedTestRuns; i++)
                {
                    var value1 = default(TType);
                    var value2 = default(TType);
                    Assert.IsTrue(value1.Equals(value2));
                }
            }
}
}
Oraz w różnym czasie domyślna wartość powinna zwracać tą samą wartość
 public abstract class EquatableRelation<TType> 
        where TType : IEquatable<TType>
{
 protected virtual void It_should_return_always_the_same_value_for_default_value_in_valueType_for_diffrent_timeTics() //super długa nazwa
{
            if (IsValueType())
            {
                Random r= new Random(12345);
                var defaultvalues = new List<TType>();
                for (int i = 0; i < NumberOfRepeatedTestRuns; i++)
                {
                    Thread.Sleep(r.Next(100,500));
                    defaultvalues.Add(default(TType));
                }
                Assert.IsTrue(defaultvalues.All(x => x.Equals(default(TType))));
            }
        }
}
Ok, to teraz masz standardy jakie powinien spełniać każdy obiekt implementujący IEquatable<WrappedString>. Możemy do tego stworzyć interfejs z tym standardem.
public interface IStandardEquatable
{
  void It_should_fulfill_IEquatable_standards();
}

 public abstract class EquatableRelation<TType>  :IStandardEquatable
        where TType : IEquatable<TType>
{
 public virtual void It_should_fulfill_IEquatable_standards()
{
            It_should_return_true_for_default_value_in_valueType();
            It_should_return_false_because_comparing_to_null();
            It_should_be_reflexive_relation();
            It_should_return_always_the_same_value_for_reflexive();
            It_should_be_coreflexive_relation_because_same_value();
            It_should_return_always_the_same_value_for_Equals();
            It_should_be_symmetric_relation();
            It_should_be_Transitive_relation();
            It_should_return_always_the_same_value_for_default_value_in_valueType();
            It_should_return_always_true_for_default_values_in_valueType();
            It_should_return_always_the_same_value_for_default_value_in_valueType_for_diffrent_timeTics();
}
}
A nasz pierwszy abstrakcyjny test
 public abstract class EquatableRelationTest<TType>  : EquatableRelation<TType>
        where TType : IEquatable<TType>

{
[Test, Explicit("Run test which are required for standardization")]
public override void It_should_fulfill_IEquatable_standards()
{
            base.It_should_fulfill_IEquatable_standards();
}
}

Jeżeli będziesz chciał przetestować int to możesz:
[TestFixture]
public class IntEquatableTest : EquatableRelationTest<int>
{
        public override  int GetItemX()
        {
            return 1;
        }
 
        public override int GetItemY()
        {
            return 1;
        }
 
        public override int GetItemZ()
        {
            return 1;
        }
}
Tak samo dla WrappedString
[TestFixture]
public class WrappedStringEquatableTest :  EquatableRelationTest<WrappedString>
{
        public override WrappedString GetItemX()
        {
            return new WrappedString("Abc");
        }
 
        public override WrappedString GetItemY()
        {
            return new WrappedString("Abc");
        }
 
        public override WrappedString GetItemZ()
        {
            return new WrappedString("Abc");
        }
}
Można skorzystać z TestCasów
[TestFixture]
public class WrappedStringEquatableTest3
{
        [TestCase("Abc", "Abc", "Abc")]
        [TestCase("Abc ", "abc", "Abc")]
        public void It_should_be_eq(string str1, string str2, string str3)
        {
            IEqStdFulfillable tuple =
                new EquatableRelationTuple(new WrappedString(str1), 
                                                          new WrappedString(str2),
                                                          new WrappedString(str3));
            tuple.It_should_fulfill_IEquatable_standards();
        }
}
Do tych testów pomocna będzie nam Tupla i Checker (2 dodatkowe wrappery).
public class EquatableRelationTuple<T> : Tuple<T, T, T>, IStandardEquatable
        where T : IEquatable<T>
{
        private readonly EquatableRelationChecker<T> checker;
 
        public EquatableRelationTuple(T item1, T item2, T item3)
            : base(item1, item2, item3)
        {
            checker = new EquatableRelationChecker<T>(item1, item2, item3);
        }
 
        public void It_should_fulfill_IEquatable_standards()
        {
            checker.It_should_fulfill_IEquatable_standards();
        }
}

 public class EquatableRelationChecker<T> : EquatableRelation<T>
        where T : IEquatable<T>
{
        private readonly T _vakue1;
        private readonly T _value2;
        private readonly T _value3;
 
        public EquatableRelationChecker(T value1, T value2, T value3)
        {
            _vakue1 = value1;
            _value2 = value2;
            _value3 = value3;
        }
 
        public override T GetItemX()
        {
            return _vakue1;
        }
 
        public override T GetItemY()
        {
            return _value2;
        }
 
        public override T GetItemZ()
        {
            return _value3;
        }
}


Brak komentarzy:

Prześlij komentarz