Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mock a class with no interface and no virtual methods in C#?

I'm writing unit tests for someone else's code that I'm not allowed to modify.

Say I have:

class BadClass
{
    public BadClass()
    {
        // the service isn't going to be running during testing
        // it also has no interface
        someFlag = AGloballyStoredService.getSomeFlag();
    }

    public bool someFlag;

}

that is used by:

class AnotherBadClass
{
    public AnotherBadClass(BadClass badClass)
    {
        someFlag = badClass.someFlag;
    }
    public bool someFlag;
}

Say I want the test:

public void AnotherBadClassConstructorTest()
{
    BadClass badClass = new BadClass();
    AnotherBadClass anotherBadClass = new AnotherBadClass(badClass);
    Assert.IsNotNull(anotherBadClass);
}

I would like to mock BadClass, but it has no interface and it fails during its constructor if the service isn't running.

Considering that I can't modify the code that I'm testing, is there a straightforward way to make this work?

If it comes down to it, I can tell them that they have to either let me modify the existing code base or accept sub 10% coverage for this module.

like image 726
Damien Avatar asked Feb 07 '23 04:02

Damien


1 Answers

Considering that I can't modify the code that I'm testing, is there a straightforward way to make this work?

No. However, I see two, non-straightforward options:

A) Use Microsoft Fakes to provide the mocks by rewriting the library. You won't have to modify the code. Example: you can modify static DateTime.Now to return the date of your choice like this:

System.Fakes.ShimDateTime.NowGet = 
() =>
{ return new DateTime(fixedYear, 1, 1); };

B) You can try to inherit the tested class and override used methods/ctors. There are two limitations though. You have to be able to instantiate/swap the instance of tested class with your mock object (by using reflection you can get to private members) and the mocked method must be virtual. In your case you would create:

class BadClassMock : BadClass
{
    public BadClassMock()
    {
    }

    public bool someFlag;

    public void InitForTest()
    {
        someFlag = true;
    }
}

And it will work, as long as you won't call the base constructor. You can use nasty FormatterServices.GetUninitializedObject() trick from here: Creating instance of type without default constructor in C# using reflection

and later:

public void AnotherBadClassConstructorTest()
{
    BadClassMock badClassMock = (BadClassMock)FormatterServices.GetUninitializedObject(typeof(BadClassMock));
    badClassMock.InitForTest();
    AnotherBadClass anotherBadClass = new AnotherBadClass(badClassMock);
    Assert.IsNotNull(anotherBadClass);
}

Yes, it is pretty big workaround. Obviously the best way is to convince the author to modify the code.

like image 72
piotrwest Avatar answered Apr 20 '23 00:04

piotrwest