AutoCodeCoverage - how it can help you find a bug

Hi

Last time I shown you what is AutoCodeCoverage. Today I would like to show you how it can help you. I'll show you how it can find small mistakes in you code.


Let's start.
Last time i shown my example solution which was covered in 100% by unit tests with AutoCodeCoverage. 
There were 3 projects - SimpleClassLibrary, SimpleClassLibrary2 and UnitTestProject1. It looked like: 



Now it looks like:

I added more empty implementations of interface Inter1Lib2 and change logic of Class2.
Old Class2 was looking like this:

namespace SimpleClassLibrary
{
  public class Class2
  {
    public void HowIWasCreated(Class1 cl)
    {
      if (cl == null)
      {
        Console.WriteLine("null");
        return;
      }

      if (cl.HowCreated() == EnumHowCreated.DefaultConstructor)
      {
        Console.WriteLine("Default");
      }
      if (cl.HowCreated() == EnumHowCreated.ParameterConstructor)
      {
        Console.WriteLine("ParameterConstructor");
      }
      if (cl.HowCreated() == EnumHowCreated.ParameterConstructorWithNull)
      {
        Console.WriteLine("ParameterConstructorWithNull");
      }
      if (cl.HowCreated() == EnumHowCreated.ParameterConstructorWithClass1Lib)
      {
        Console.WriteLine("ParameterConstructorWithClass1Lib");
      }
    }
  }
}

New Class2 is looking like this:
namespace SimpleClassLibrary
{
  public class Class2
  {
    Dictionary<EnumHowCreated, Class1> dic = new Dictionary<EnumHowCreated, Class1>();
    public void HowIWasCreated(Class1 cl)
    {
      dic[cl.HowCreated()] = cl;

      if (cl == null)
      {
        Console.WriteLine("null");
        return;
      }

      if (cl.HowCreated() == EnumHowCreated.DefaultConstructor)
      {
        Console.WriteLine("Default");
      }
      if (cl.HowCreated() == EnumHowCreated.ParameterConstructor)
      {
        Console.WriteLine("ParameterConstructor");
      }
      if (cl.HowCreated() == EnumHowCreated.ParameterConstructorWithNull)
      {
        Console.WriteLine("ParameterConstructorWithNull");
      }
      if (cl.HowCreated() == EnumHowCreated.ParameterConstructorWithClass1Lib)
      {
        Console.WriteLine("ParameterConstructorWithClass1Lib");
      }

      foreach(var key  in dic.Keys)
      {
        if (!key.Equals(dic[key].HowCreated()))
        {
          throw new Exception($"{key} has bad value");
        }
      }
    }
  }
}
So run old unit test looking like this (i've added Console.WriteLine to show exceptions :) ):
    [TestMethod]
    public void SimpleClassLibraryWithOptions()
    {
      AutoCodeCoverer autoCodeCoverer = new AutoCodeCoverer(new AutoCoverOptions()
      {
        AllowNullsAsConstractorParameter = true,
        AllowNullsAsMethodParameter = true,
        AllowSearchInMicrosoftAssembly = false,
        CoverFields = true,
        CoverProperties = true,
        InvokeMethods = true,
        SearchImplentationInSourceAssembly = true,
        TopParameterCombinationsForCreateInstances = 100,
        TopParameterCombinationsForInvokeMethods = 100,
        TopCountOfSameObjectInstances = 100
      });
      autoCodeCoverer.RunCovererOnAssembly("SimpleClassLibrary", "SimpleClassLibrary2");
      var errors = autoCodeCoverer.GetErrors();
      foreach(var error in errors)
      {
        Console.WriteLine($"{error.Type}, {error.Parameters}, {error.ErrorType},{error.Exception}");
      }

      Assert.AreEqual(errors.Count(), 0);
    }
Unfortunately, how could you suppose test failed. It wrote on console exception:
System.NullReferenceException: Object reference not set to an instance of an object.
   at SimpleClassLibrary.Class2.HowIWasCreated(Class1 cl) 
in C:\Users\mariu\source\repos\SimpleClassLibrary\SimpleClassLibrary\Class2.cs:line 11
I show you again Class2 now with line numbers:
4 namespace SimpleClassLibrary
5 {
6   public class Class2
7   {
8     Dictionary<EnumHowCreated, Class1> dic = new Dictionary<EnumHowCreated, Class1>();
9     public void HowIWasCreated(Class1 cl)
10    {
11      dic[cl.HowCreated()] = cl;
12
13      if (cl == null)
14      {
15        Console.WriteLine("null");
16        return;
17      }
18
19      if (cl.HowCreated() == EnumHowCreated.DefaultConstructor)
20      {
21        Console.WriteLine("Default");
22      }
23      if (cl.HowCreated() == EnumHowCreated.ParameterConstructor)
24      {
25        Console.WriteLine("ParameterConstructor");
26      }
27      if (cl.HowCreated() == EnumHowCreated.ParameterConstructorWithNull)
28      {
29        Console.WriteLine("ParameterConstructorWithNull");
30      }
31      if (cl.HowCreated() == EnumHowCreated.ParameterConstructorWithClass1Lib)
32      {
33        Console.WriteLine("ParameterConstructorWithClass1Lib");
34      }
35
36      foreach(var key  in dic.Keys)
37      {
38        if (!key.Equals(dic[key].HowCreated()))
39        {
40          throw new Exception($"{key} has bad value");
41        }
42      }
43    }
44  }
}
Visual Studio didn't suggest that in line 11 there is possible to thrown null reference exception but when parameter Class1 is null NullReferenceException will be thrown. After fix problems my Class2 looks like:
4 namespace SimpleClassLibrary
5 {
6   public class Class2
7   {
8     Dictionary<EnumHowCreated, Class1> dic = new Dictionary<EnumHowCreated, Class1>();
9     public void HowIWasCreated(Class1 cl)
10    {
11      if (cl == null)
12      {
13        Console.WriteLine("null");
14        return;
15      }
16
17      dic[cl.HowCreated()] = cl;
18
19      if (cl.HowCreated() == EnumHowCreated.DefaultConstructor)
20      {
21        Console.WriteLine("Default");
22      }
23      if (cl.HowCreated() == EnumHowCreated.ParameterConstructor)
24      {
25        Console.WriteLine("ParameterConstructor");
26      }
27      if (cl.HowCreated() == EnumHowCreated.ParameterConstructorWithNull)
28      {
29        Console.WriteLine("ParameterConstructorWithNull");
30      }
31      if (cl.HowCreated() == EnumHowCreated.ParameterConstructorWithClass1Lib)
32      {
33        Console.WriteLine("ParameterConstructorWithClass1Lib");
34      }
35
36      foreach(var key  in dic.Keys)
37      {
38        if (!key.Equals(dic[key].HowCreated()))
39        {
40          throw new Exception($"{key} has bad value");
41        }
42      }
43    }
44  }
}
Now test is green but next I set MaxParalelism for invoking methods to value 2.
    [TestMethod]
    public void SimpleClassLibraryWithOptions()
    {
      AutoCodeCoverer autoCodeCoverer = new AutoCodeCoverer(new AutoCoverOptions()
      {
        AllowNullsAsConstractorParameter = true,
        AllowNullsAsMethodParameter = true,
        AllowSearchInMicrosoftAssembly = false,
        CoverFields = true,
        CoverProperties = true,
        InvokeMethods = true,
        SearchImplentationInSourceAssembly = true,
        TopParameterCombinationsForCreateInstances = 100,
        TopParameterCombinationsForInvokeMethods = 100,
        TopCountOfSameObjectInstances = 100,
        MaxDegreeOfParallelismForCombinationOfParametersMethodInvokes=2
      });
      autoCodeCoverer.RunCovererOnAssembly("SimpleClassLibrary", "SimpleClassLibrary2");
      var errors = autoCodeCoverer.GetErrors();
      foreach(var error in errors)
      {
        Console.WriteLine($"{error.Type}, {error.Parameters}, {error.ErrorType},{error.Exception}");
      }

      Assert.AreEqual(errors.Count(), 0);
    }
Unfortunately, failed again. It wrote on console exception:
Exception has been thrown by the target of an invocation. ---> 
System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
   at System.ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion()
   at System.Collections.Generic.Dictionary`2.KeyCollection.Enumerator.MoveNext()
   at SimpleClassLibrary.Class2.HowIWasCreated(Class1 cl) 
in C:\Users\mariu\source\repos\SimpleClassLibrary\SimpleClassLibrary\Class2.cs:line 36
As you can know foreach cannot be used when your collection can be changed while using foreach. This is the reason for the exception in line 36. After fix problem my Class2 looks like:
4 namespace SimpleClassLibrary
5 {
6   public class Class2
7   {
8     Dictionary<EnumHowCreated, Class1> dic = new Dictionary<EnumHowCreated, Class1>();
9     public void HowIWasCreated(Class1 cl)
10    {
11      if (cl == null)
12      {
13        Console.WriteLine("null");
14        return;
15      }
16
17      dic[cl.HowCreated()] = cl;
18
19      if (cl.HowCreated() == EnumHowCreated.DefaultConstructor)
20      {
21        Console.WriteLine("Default");
22      }
23      if (cl.HowCreated() == EnumHowCreated.ParameterConstructor)
24      {
25        Console.WriteLine("ParameterConstructor");
26      }
27      if (cl.HowCreated() == EnumHowCreated.ParameterConstructorWithNull)
28      {
29        Console.WriteLine("ParameterConstructorWithNull");
30      }
31      if (cl.HowCreated() == EnumHowCreated.ParameterConstructorWithClass1Lib)
32      {
33        Console.WriteLine("ParameterConstructorWithClass1Lib");
34      }
35
36      for(int i = 0; i < dic.Keys.Count(); i++)
37      {
38        var key = dic.Keys.ElementAt(i);
39        if (!key.Equals(dic[key].HowCreated()))
40        {
41          throw new Exception($"{key} has bad value");
42        }
43      }
44    }
45  }
46}
Now test is green again but Dictionary isn't good for multithread invoking, so I set parallelism to higher value - 100.
    [TestMethod]
    public void SimpleClassLibraryWithOptions()
    {
      AutoCodeCoverer autoCodeCoverer = new AutoCodeCoverer(new AutoCoverOptions()
      {
        AllowNullsAsConstractorParameter = true,
        AllowNullsAsMethodParameter = true,
        AllowSearchInMicrosoftAssembly = false,
        CoverFields = true,
        CoverProperties = true,
        InvokeMethods = true,
        SearchImplentationInSourceAssembly = true,
        TopParameterCombinationsForCreateInstances = 100,
        TopParameterCombinationsForInvokeMethods = 100,
        TopCountOfSameObjectInstances = 100,
        MaxDegreeOfParallelismForCombinationOfParametersMethodInvokes=100
      });
      autoCodeCoverer.RunCovererOnAssembly("SimpleClassLibrary", "SimpleClassLibrary2");
      var errors = autoCodeCoverer.GetErrors();
      foreach(var error in errors)
      {
        Console.WriteLine($"{error.Type}, {error.Parameters}, {error.ErrorType},{error.Exception}");
      }

      Assert.AreEqual(errors.Count(), 0);
    }
Unfortunately, failed again. It wrote on console exception:
Exception has been thrown by the target of an invocation. ---> 
System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
   at System.ThrowHelper.ThrowKeyNotFoundException()
   at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
   at SimpleClassLibrary.Class2.HowIWasCreated(Class1 cl) 
in C:\Users\mariu\source\repos\SimpleClassLibrary\SimpleClassLibrary\Class2.cs:line 39
I suggested that Dictionary isn't thread safe collection. After change it into ConcurrentDictionary problem never came back.
4 namespace SimpleClassLibrary
5 {
6   public class Class2
7   {
8     ConcurrentDictionary<EnumHowCreated, Class1> dic = new ConcurrentDictionary<EnumHowCreated, Class1>();
9     public void HowIWasCreated(Class1 cl)
10    {
11      if (cl == null)
12      {
13        Console.WriteLine("null");
14        return;
15      }
16
17      dic[cl.HowCreated()] = cl;
18
19      if (cl.HowCreated() == EnumHowCreated.DefaultConstructor)
20      {
21        Console.WriteLine("Default");
22      }
23      if (cl.HowCreated() == EnumHowCreated.ParameterConstructor)
24      {
25        Console.WriteLine("ParameterConstructor");
26      }
27      if (cl.HowCreated() == EnumHowCreated.ParameterConstructorWithNull)
28      {
29        Console.WriteLine("ParameterConstructorWithNull");
30      }
31      if (cl.HowCreated() == EnumHowCreated.ParameterConstructorWithClass1Lib)
32      {
33        Console.WriteLine("ParameterConstructorWithClass1Lib");
34      }
35
36      for(int i = 0; i < dic.Keys.Count(); i++)
37      {
38        var key = dic.Keys.ElementAt(i);
39        if (!key.Equals(dic[key].HowCreated()))
40        {
41          throw new Exception($"{key} has bad value");
42        }
43      }
44    }
45  }
46}
I hope that AutoCodeCoverage can help you. Thanks for your attentions.

Komentarze

Popularne posty z tego bloga

Czarna lista produktów zawierających utwardzony tłuszcz roślinny. Aktualizacja 03.08.2013

Najlepszy gyros we Wrocławiu - Petra Gyros

[Part 1] Is c# safe? Are Internal class accessible only from the same assembly?