Pregunta

Consider the following class

public class Entity {
  public void Foo() { ... }
  internal void Bar() { ... }
}

As you see it has a public method and an internal method. Now, I would like to create an interface which will allow me to mock this class in tests (both in this assembly and in others). I rewrite my code as following:

public interface IEntity {
  void Foo();
}

internal class Entity : IEntity {
  public void Foo() { ... }
  public void Bar() { ... }
}

However, this creates another issue. When using the class in another method in the same assembly I can't call Bar anymore:

public class OtherClass {
  public void SomeMethod(IEntity entity) {
    entity.Bar(); // error!
  }
}

Rewriting the code like this:

public class OtherClass {
  public void SomeMethod(IEntity entity) {
    (entity as Entity).Bar(); // error in test!
  }
}

will trigger an error in the unit test that triggers SomeMethod. How do I rewrite my code so that I can still use internal methods in the same assembly and yet only expose public members to other assemblies?

Update: Class OtherClass is a utility class that needs to operate on internals of the Entity class, which are not exposed to the users directly. However, this class itself is exposed to users, so they indirectly have access to internals of the Entity. This is desired as SomeMethod will perform necessary checks to ensure that users won't screw up the internal state of an Entity object.

¿Fue útil?

Solución 2

Edited:

Extend your IEntity interface with an internal ITestEntity interface for testing:

public interface IEntity
{
    //Implementation

}

internal interface ITestEntity : IEntity
{
    void TestMethod();
}


class Entity: ITestEntity
{
   //
}

Otros consejos

How do I rewrite my code so that I can still use internal methods in the same assembly and yet only expose public members to other assemblies?

Make the interface internal and then explicitly implement it.

internal interface ITest
{
  void Foo();
  void Bar();
}

public class Thing : ITest
{
  void ITest.Foo() { this.Foo(); }
  void ITest.Bar() { this.Bar(); }
  public Foo() { ... }
  internal Bar() { ... }
}

So now public class Thing has only one public method Foo. Code outside the assembly cannot call Bar either directly or via a conversion to the interface because Bar and ITest are both internal.

I note that a base class must be at least as visible as its deriving class. Not so interfaces. Few people know that this is legal:

class C : C.I
{
    private interface I {} 
}

A class can implement an interface that only it can see! This is a bit of a strange thing to do but there are some situations where it makes sense.

I assume you want to call the internal method only in your unit tests right? Because otherwise, exposing the method to other assemblies would require to replace internal with public of cause.

For unit testing in general, it is always a good pattern if you DO NOT test private or internal methods. A Unit test should actually just test the public interface and any business class should provide public methods which do whatever internally... So to test your internal methods, you would have to test the public representation of it.

But anyways. If you want to test private or internal accessor, Visual Studio Test projects usually can generate accessor wrappers for you. You would have to call the accessor ctor instead of the normal ctor of your class. When you do so, you can access any private or internal methods/properties of that instance.

Find more information about those generated types here: http://msdn.microsoft.com/en-us/library/bb385974(v=vs.100).aspx

:edit: after discussion, I have an Example for you of how to use moq together with Unity and UnityAutoMoq (3 different Nugets). Lets say your class looks like this:

public class MyClassWithInternalMethod
{
    internal object GetSomething()
    {
        return null;
    }
}

To test this you can mock it like this:

using (var moqUnityContainer = new UnityAutoMoqContainer())
{
    moqUnityContainer.GetMock<MyClassWithInternalMethod>().Setup(p => p.GetSomething()).Returns(null);
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top