AutoCodeCoverage - nuget package for covering .net code with tests

Hi

I was wondering for some time about code coverage in c#. I'm using a lot of DTO objects which should be covered by unit tests. I thought that it should be covered by default.
I wrote simple project FakeCodeCoverage. It used reflection to cover the simplest way in constructors and methods. It was not enough for me. It was really only fake coverage.

I abandoned this project but I started new project AutoCodeCoverage. This is different from the previous one because it tries cover all statements, branches and conditions. As result you can get all exceptions which was thrown while invoking methods, constructors, properties, etc. It allows to inject implementation of interface or instance of type which will be used at all times when this type will be exist as parameter.

In DTOs or simple projects AutoCodeCoverage could cover up to 100% lines of your code.

It is published on nuget

https://www.nuget.org/packages/AutoCodeCoverage

Actually it support .net4.x, .netcore 2.0 and .netstandard2.0.

I will now show a simple example.
In my example solution are 3 projects - SimpleClassLibrary, SimpleClassLibrary2 and UnitTestProject1. It looks like: 



Below I show you all my clases.


namespace SimpleClassLibrary
{
  public class Class1
  {
    EnumHowCreated created = EnumHowCreated.NA;
    public Class1()
    {
      created = EnumHowCreated.DefaultConstructor;
    }

    public Class1(Inter1Lib2 inter1Lib2)
    {
      if (inter1Lib2 == null)
        created = EnumHowCreated.ParameterConstructorWithNull;
      else if (inter1Lib2 is Class1Lib2)
      {
        created = EnumHowCreated.ParameterConstructorWithClass1Lib;
      }
      else
        created = EnumHowCreated.ParameterConstructor;
    }

    public void HowIWasCreated()
    {
      if (created == EnumHowCreated.DefaultConstructor)
      {
        Console.WriteLine("Default");
      }
      if (created == EnumHowCreated.ParameterConstructor)
      {
        Console.WriteLine("ParameterConstructor");
      }
      if (created == EnumHowCreated.ParameterConstructorWithNull)
      {
        Console.WriteLine("ParameterConstructorWithNull");
      }
      if (created == EnumHowCreated.ParameterConstructorWithClass1Lib)
      {
        Console.WriteLine("ParameterConstructorWithClass1Lib");
      }
    }

    public EnumHowCreated HowCreated()
    {
      return created;
    }
  }
}



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");
      }
    }
  }
}



namespace SimpleClassLibrary2
{
  public class Class1Lib2 : Inter1Lib2
  {
    private DateTime GetDate()
    {
      return DateTime.Now;
    }
  }
}


namespace SimpleClassLibrary2
{
  public class Class2Lib2 : Inter1Lib2
  {
    string property1 { get; set; }
    private string property2 { get; set; }
  }
}


namespace SimpleClassLibrary2
{
  public interface Inter1Lib2
  {
  }
}
Covarage by dotCover looks like:

Cool, right? 

I wrote 2 tests, first simple without options (all by default) 


    [TestMethod]
    public void SimpleClassLibrary()
    {
      AutoCodeCoverage.AutoCodeCoverer autoCodeCoverer = new AutoCodeCoverage.AutoCodeCoverer();
      autoCodeCoverer.RunCovererOnAssembly("SimpleClassLibrary","SimpleClassLibrary2");
      var errors = autoCodeCoverer.GetErrors();
      Assert.AreEqual(errors.Count(), 0);
    }
It didn't cover all lines.
Second one tests cover all line and it looks like:


    [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 = 4
      });
      autoCodeCoverer.RunCovererOnAssembly("SimpleClassLibrary","SimpleClassLibrary2");
      var errors = autoCodeCoverer.GetErrors();
      Assert.AreEqual(errors.Count(), 0);
    }
As you can see there are a lot of options which you can set  yourself. 

By default, I have chosen the optimal settings for simple applications.


  
    bool SearchImpelentationInSourceAssembly = false;
    bool TryCoverBaseExternal = false;
    bool InvokeMethods = true;
    bool CoverProperties = true;
    bool CoverFields = true;
    bool AllowSearchInMicrosoftAssembly = false;
    int MaxDegreeOfParallelismForCreateInstances = 1;
    int MaxDegreeOfParallelismForCombinationOfParametersMethodInvokes = 1;
    int MaxDegreeOfParallelismForMethodsInvokes = 1;
    int TopParameterCombinationsForCreateInstances = 10;
    int TopParameterCombinationsForInvokeMethods = 10;
    bool AllowNullsAsMethodParameter = false;
    bool AllowNullsAsConstractorParameter = false;
    bool AllowRandomizeParametersWithTopCount = false;
    int TopCountOfSameObjectInstances = 1;

AutoCodeCoverage can find implementations of interfaces or inheriting classes in assembly where this type was declared. You can disable method invoking, covering properties or fields. Also you can set parallelism for creating instances or invoking methods. 
E.g. If your constructor or method contains 2 interfaces as parameters and they are implemented by 5 classes it creates 25 combinations of parameters. 
You can limit it, by default it is set to 10. 
The last one option "TopCountOfSameObjectInstances" is the newest option.  It allows to use different objects of the same class as parameter. 
E.g. If you have different logic controlled by the object parameter like below

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");
      }
    }
  }
}

AutoCodeCoverage tries use different Class1 objects when TopCountOfSameObjectInstances is set to  greater than 1.


I mentioned that you can inject instance of class or implementation of interface. It is very simple. 

      autoCodeCoverer.SetInstanceToInject(typeof(Inter1Lib2), new Class1Lib2());

When AutoCodeCover has to create instance of Inter1Lib2 (interface) implementation it uses your declared by you object.

I hope you will try my library and it help you to discover simple mistakes.

Please, give me feedback/suggestions. 

Komentarze

Popularne posty z tego bloga

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

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

Olej palmowy w produktach