2

The Situation

Suppose I have a class with an internal method:

class MyClass : IMyClass
{
    public void PublicMethod() { ... }
    internal void InternalMethod() { ... }
}

that implements an interface:

interface IMyClass
{
    void PublicMethod() { ... }
}

And a container class that holds an instance of MyClass and other concrete classes:

class ContainerClass
{
    public MyClass myClass;
    ...
}

ContainerClass is used by various assemblies. When used within the same assembly as MyClass the calling method can invoke containerClass.myClass.InternalMethod()


The Problem

In an effort to make things more easily mockable for testing, I am now trying to replace the ContainerClass's properties with interfaces rather than concrete classes, e.g.:

class ContainerClass
{
    public IMyClass myClass;
    ...
}

But, if I do that, InternalMethod can no longer be invoked using containerClass.myClass.InternalMethod() because the method doesn't exist on the interface... nor can it, because it is internal. However, I cannot make the interface internal because ContainerClass is used by other assemblies as well.

Let's assume that I cannot change the internal access to that method. Is there another way?


EDIT: This is different from How to Mock the Internal Method of a class? because I am not trying to mock the internal method, I am trying to keep it exposed when replacing a concrete class (MyClass) with an interface (IMyClass).

Kyle G.
  • 870
  • 2
  • 10
  • 22
  • @elgonzo My goal is not to mock the internal method (my tests don't require it to be mocked). This issue is caused by replacing the concrete class `MyClass` with the interface `IMyClass`: the internal method becomes invisible/inaccessible because it is not exposed through the interface. – Kyle G. Mar 07 '18 at 19:21
  • Wait... so you want to mock parts of MyClass while not mocking other parts of MyClass? Something does not seem to add up, or i completely misunderstand you... –  Mar 07 '18 at 19:25
  • @elgonzo I'm likely just not explaining myself very well. The question is not about mocking, per se. The issue came up while trying to make things easier to mock (i.e. by using DI with interfaces rather than tightly coupling `ContainerClass` to `MyClass`) because other (legacy) code calls the internal method on `MyClass` *through* an instance of the `ContainerClass`. – Kyle G. Mar 07 '18 at 19:29
  • Ah, okay. Got you now. Would it be feasible to use some abstract base class instead of an interface? The base class could provide the (virtual) internal method. And you would then have two different implementations based on this abstract base class; one normal "production class", and another implementation for testing (with or without individual overrides of the public and/or internal methods) –  Mar 07 '18 at 19:33
  • Hmmm, you might be on to something there. I'm trying another possible solution out right now, but if that doesn't work I'll try yours. – Kyle G. Mar 07 '18 at 19:36
  • @InBetween unfortunately it cannot (that was actually my first thought). elgonzo's suggesting looks to be working well so far! – Kyle G. Mar 07 '18 at 20:16
  • I was asking because in your question it *is* internal, seemed weird. – InBetween Mar 07 '18 at 20:17
  • I probably could have been more clear: `ContainerClass` gets used both internally *and* externally. My understanding is that the `internal` modifier on the `InternalMethod()` protects that method from being called from outside the assembly, regardless of *how* it gets called. – Kyle G. Mar 07 '18 at 20:25

1 Answers1

3

An option is to create two interfaces, one public and one internal:

public interface IMyClassPublic
{
    void PublicMethod();
}

internal interface IMyClassInternal
{
    void InternalMethod();
}

Implement both of them in MyClass (you need to implement IMyClassInternal explicitly since it's internal):

public class MyClass : IMyClassPublic, IMyClassInternal
{
    public void PublicMethod() {  }
    void IMyClassInternal.InternalMethod() {  }
}

And change ContainerClass so that it exposes both interfaces separately (and probably make myClass private):

public class ContainerClass
{
    private MyClass myClass;
    public IMyClassPublic myClassPublic
    {
        get
        {
            return myClass;
        }
    }

    internal IMyClassInternal myClassInternal
    {
        get
        {
            return myClass;
        }
    }
}

Now you can mock both of them, use the internal method as containerClass.myClassInternal.InternalMethod() and the public method as containerClass.myClassPublic.PublicMethod().

jesm00
  • 115
  • 1
  • 7