2016-05-17 4 views
1

Я пытаюсь проверить бизнес-логику в запросах в службах. Поэтому я не хочу, чтобы мои тесты имели реальный доступ к базе данных, потому что это модульные тесты, а не интеграционные тесты.Тестирование службы с использованием Entity Framework без инъекции зависимости

Итак, я сделал простой пример моего контекста и как я пытаюсь его подкрепить.

У меня есть юридическое лицо

public class SomeEntity 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

и сервисный

public class Service 
{ 
    public int CountSomeEntites() 
    { 
     using (var ctx = new Realcontext()) 
     { 
      int result = ctx.SomeEntities.Count(); 
      return result; 
     } 
    } 
} 

И это реальный контекст

public partial class Realcontext : DbContext 
{ 
    public virtual DbSet<SomeEntity> SomeEntities { get; set; } 

    public Realcontext() : base("name=Realcontext") 
    { 
     InitializeContext(); 
    } 

    partial void InitializeContext(); 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     throw new UnintentionalCodeFirstException(); 
    } 
} 

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

Это фальшивка контекст

public class FakeContext : DbContext 
{ 
    public DbSet<SomeEntity> SomeEntities { get; set; } 

    public FakeContext() 
    { 
    } 
} 

И, наконец, класс тест

[TestClass] 
public class ServiceTests 
{ 
    [TestMethod] 
    public void CountEmployee_ShoulReturnCorrectResult() 
    { 
     using (ShimsContext.Create()) 
     { 
      ShimRealcontext.Constructor = context => GenerateFakeContext(); 
      ShimDbContext.AllInstances.Dispose =() => DummyDispose(); 

      Service service = new Service(); 
      int result = service.CountSomeEntites(); 

      Assert.AreEqual(result, 2); 
     } 
    } 

    private FakeContext GenerateFakeContext() 
    { 
     FakeContext fakeContext = new FakeContext(); 
     fakeContext.SomeEntities.AddRange(new[] 
     { 
      new SomeEntity {Id = 1, Name = "entity1"}, 
      new SomeEntity {Id = 2, Name = "entity2"} 
     }); 
     return fakeContext; 
    } 
} 

Когда я запустить тест, то RealContext конструктор возвращается правильно, FakeContext построен в методе GenerateFakeContext(), она содержит 2 SomeEntities и возвращается, но сразу после этого, на службе, свойство SomeEntities переменной ctx равно нулю.

Это потому, что моя переменная ctx объявлена ​​как new RealContext()? Но вызов конструктора RealContext возвращает FakeContext(), так что переменная не должна быть типа FakeContext?

Я что-то не так? Или есть ли другой способ проверить службу без доступа к реальной базе данных?

+0

В наших тестах мы позаимствовали издевательства из источника EF. Он находится под лицензией Apache. – Eris

+0

Спасибо! Я не знал этого инструмента. Я посмотрю –

ответ

0

У меня была ситуация с simlair, и я решил ее с конфигурацией сборки и условной компиляцией. Это не лучшее решение, но это сработало для меня и решило проблему. Вот расписка:

1. Создать DataContext интерфейс

Прежде всего, необходимо создать интерфейс, который будет реализован как контекст Классе вы собираетесь использовать. Пусть он называется просто «IMyDataContext». В нем вам нужно описать все DbSets, к которым вам нужно иметь доступ.

public interaface IMyDataContext 
{ 
    DbSet<SomeEntity> SomeEntities { get; set; } 
} 

И оба ваши контекстные классы должны impelemt его:

public partial class RealDataContext : DataContext, IMyDataContext 
{ 
    DbSet<SomeEntity> SomeEntities { get; set; } 

    /* Contructor, Initialization code, etc... */ 
} 

public class FakeDataContext : DataContext, IMyDataContext 
{ 
    DbSet<SomeEntity> SomeEntities { get; set; } 

    /* Mocking, test doubles, etc... */ 
} 

Кстати, вы можете даже сделать чтение Недвижимости Болгарии Недвижимости только на уровне интерфейса.

2. Добавьте 'Test' конфигурации сборки

Here вы можете найти, как добавить новую конфигурацию сборки. Я назвал свой конфигуратор «Тест».После создания новой конфигурации перейдите к свойствам проекта DAL, в разделе «Сборка» на левой панели. В раскрывающемся списке «Конфигурация» выберите конфигурацию, которую вы только что создали, и введите «Символы условной компиляции» «TEST».

3. инкапсулировать контекст инъекции

Чтобы было ясно, что мой подход еще метод/свойство на основе DI решение =)

Так что теперь мы должны реализовать некоторые инъекции кода. Для простоты вы можете добавить его прямо в свою службу или извлечь в другой класс, если вам нужно больше абстракции. Основная идея - использовать условные компиляции, а не структуру IoC.

public class Service 
{ 
    // Injector method 
    private IMyDataContext GetContext() { 
     // Here is the main code 

#if TEST // <-- In 'Test' configuration 
      // we will use fake context 
      return new FakeDataContext(); 
#else 
      // in any other case 
      // we will use real context 
      return new RealDataContext(); 
#endif 

    } 

    public int CountSomeEntites() 
    { 
     // the service works with interface and does know nothing 
     // about the implementation 

     using (IMyDataContext ctx = GetContext()) 
     { 
      int result = ctx.SomeEntities.Count(); 
      return result; 
     } 
    } 
} 

Ограничение

Описанный подход решает проблему вы описали, но имеет ограничение: так как позволяет IoC переключаться контексты динамически во время выполнения условного complation требует, чтобы вы перекомпиляции решение.

В моем случае это не проблема - мой код не покрывается испытаниями на 100%, и я не запускаю их в каждой сборке. Обычно я запускаю тесты только до начала кода, поэтому очень легко переключить конфигурацию сборки в VS, запустить тесты, убедиться, что ничего не сломалось, а затем вернуться в режим отладки. В режиме выпуска вам также не нужно запускать тест. Даже если вам нужно - вы можете нарушить конфигурацию «Сконструировать тестовый режим сборки» и продолжать использовать одно и то же решение.

Другая проблема заключается в том, что если у вас есть непрерывная интеграция - вам нужно сделать дополнительную настройку на своем сервере сборки. Здесь у вас есть два способа:

  • Установите два определения сборки: один для выпуска и один для испытаний. Если ваш сервер настроен на автоматическое освобождение, вам нужно быть осторожным, потому что сбой теста будет показан во втором при первом развертывании.
  • Задайте определение сложной сборки, которая впервые формирует ваш код в тестовой конфигурации, запускает проверку и, если все в порядке, затем перекомпилирует код в целевой конфигурации и подготовится к развертыванию.

Таким образом, как любое решение, это еще один компромисс между простотой и гибкостью.

UPDATE

Через некоторое время я понимаю, что так, как я описал выше, является очень тяжелым. Я имею в виду - строить конфигурации. В случае только двух реализаций IDataContext: «Core» и «Fake» вы можете просто использовать bool paramenter и простые ветви if/else вместо директив компиляции #if/#else/#endif и всех головных болей, настраивающих ваш сервер сборки.

Если у вас более двух реализаций - вы можете использовать перечисление и блок switch. Суть здесь заключается в том, чтобы определить, что вы вернете в случае default, или если значение отсутствует в диапазоне перечислений.

Но главное преимущество такого подхода заключается в том, что вы не можете больше привязываться к времени компиляции. Параметр инжектора можно было изменить в любое время, например, используя web.config и ConfigurationManager. С его помощью вы можете переключать контекст данных во время выполнения.

 Смежные вопросы

  • Нет связанных вопросов^_^