Как создать интерфейс, который сохраняет некоторые методы внутренними для тестирования в C#?

рассмотрим следующий класс

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

как вы видите, у него есть public способ и internal метод. Теперь я хотел бы создать интерфейс, который позволит мне издеваться над этим классом в тестах (как в этой сборке, так и в других). Я переписываю свой код следующим образом:

public interface IEntity {
  void Foo();
}

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

однако это создает еще одну проблему. При использовании класса в другом методе в той же сборке я не могу вызвать Bar больше:

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

переписывание кода вот так:

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

вызовет ошибку в модульном тесте, которая запускает SomeMethod. Как переписать мой код, чтобы я все еще мог использовать internal методы в той же сборке и все же предоставляют только открытые члены другим сборкам?

обновление: класс OtherClass - это служебный класс, который должен работать на ВКУ Entity класс, которые не подвергаются непосредственному воздействию пользователей. Однако сам этот класс доступен пользователям, поэтому они косвенно иметь доступ к внутренним Entity. Это желательно как SomeMethod будет выполнять необходимые проверки, чтобы убедиться, что пользователи не испортят внутреннее состояние

3 ответов


редактировать:

продлить IEntity интерфейс с внутренним ITestEntity интерфейс для тестирования:

public interface IEntity
{
    //Implementation

}

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


class Entity: ITestEntity
{
   //
}

как я могу переписать свой код, чтобы я мог использовать внутренние методы в той же сборке и все же предоставлять только открытые члены другим сборкам?

сделайте интерфейс внутренним, а затем явно реализуйте его.

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() { ... }
}

Итак, теперь открытый класс Thing имеет только один публичный метод Foo. Код вне сборки не может вызвать Bar либо напрямую, либо через преобразование в интерфейс, потому что Bar и ITest оба внутренний.

я отмечаю, что a базовый класс должен быть по крайней мере таким же видимым, как его производный класс. Не совсем так. Мало кто знает, что это законно:

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

класс может реализовать интерфейс, который только он может видеть! Это немного странно, но есть некоторые ситуации, когда это имеет смысл.


Я предполагаю, что вы хотите вызвать внутренний метод только в модульных тестах верно? Потому что в противном случае для предоставления метода другим сборкам потребуется заменить internal на public of cause.

для модульного тестирования в целом, это всегда хороший шаблон, если вы не тестируете частные или внутренние методы. Модульный тест должен фактически просто тестировать открытый интерфейс, и любой бизнес-класс должен предоставлять общедоступные методы, которые делают все внутри... Итак, чтобы проверить свой внутренний методы, вы должны были бы проверить публичное представление его.

но в любом случае. Если вы хотите протестировать частный или внутренний доступ, тестовые проекты Visual Studio обычно могут создавать для вас оболочки доступа. Вам нужно будет вызвать ctor accessor вместо обычного ctor вашего класса. При этом вы можете получить доступ к любым частным или внутренним методам / свойствам этого экземпляра.

подробнее об этих сгенерированных типах см. здесь: http://msdn.microsoft.com/en-us/library/bb385974 (v=против 100).aspx

: edit: после обсуждения у меня есть пример для вас, как использовать moq вместе с Unity и UnityAutoMoq (3 разных Nugets). Скажем, ваш класс выглядит так:

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

чтобы проверить это, вы можете издеваться над этим так:

using (var moqUnityContainer = new UnityAutoMoqContainer())
{
    moqUnityContainer.GetMock<MyClassWithInternalMethod>().Setup(p => p.GetSomething()).Returns(null);
}