2015-04-09 2 views
1

Я только начинаю изучать модульное тестирование и насмешку. Я провел весь день, читая разные учебники, пытаясь найти лучшее, на что можно практиковаться. Я остановился на Testing with a mocking framework (EF6 onwards), так как он использует EF6 (современный), а также то, что кажется очень популярным фальшивым фреймворком (Moq). Кроме того, он довольно ваниль и размещен на веб-сайте MSDN. Он должен быть порядочным, не так ли?Почему мой фиктивный набор пуст?

Я настроил проект точно так, как указано в примере, и я запускаю отладчик с помощью тестовых примеров, чтобы убедиться, что я понимаю, что происходит. Тест я работаю через это следующим образом:

[TestClass] 
public class QueryTests 
{ 
    [TestMethod] 
    public void GetAllBlogs_orders_by_name() 
    { 
     var data = new List<Blog> 
     { 
      new Blog { Name = "BBB" }, 
      new Blog { Name = "ZZZ" }, 
      new Blog { Name = "AAA" }, 
     }.AsQueryable(); 

     var mockSet = new Mock<DbSet<Blog>>(); 
     mockSet.As<IQueryable<Blog>>().Setup(m => m.Provider).Returns(data.Provider); 
     mockSet.As<IQueryable<Blog>>().Setup(m => m.Expression).Returns(data.Expression); 
     mockSet.As<IQueryable<Blog>>().Setup(m => m.ElementType).Returns(data.ElementType); 
     mockSet.As<IQueryable<Blog>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator()); 

     var mockContext = new Mock<BloggingContext>(); 
     mockContext.Setup(c => c.Blogs).Returns(mockSet.Object); 

     var service = new BlogService(mockContext.Object); 
     //test code here 
     var blogs = service.GetAllBlogs(); 

     Assert.AreEqual(3, blogs.Count); 
     Assert.AreEqual("AAA", blogs[0].Name); 
     Assert.AreEqual("BBB", blogs[1].Name); 
     Assert.AreEqual("ZZZ", blogs[2].Name); 
    } 
} 

Это очень просто, и я привожу к считаем, что я понимание модульного тестирования и насмешливые рамок. Круто! Я решил провести эксперимент для проверки себя, вставив service.AddBlog("ADO.NET Blog", "http://blogs.msdn.com/adonet"); (из предыдущего примера) в вышеупомянутый TestMethod, сразу после того, как служба была создана.

Я ожидаю, что, пройдя мимо var blogs = service.GetAllBlogs();, блоги должны содержать мою новую запись, но это не так. Он содержит только 3 из инициализации data.

Что здесь происходит? Не должен ли этот код вставить запись блога в объект data и, таким образом, потянуть его при вызове GetAllBlogs()? Возможно, я неправильно понимаю идею издевательств?

+2

Издевательский 'DbSet ', как известно, сложно.Я настоятельно рекомендую использовать шаблон репозитория и тестировать репозитории или комбинировать шаблон репозитория с базой данных в памяти, которую вы можете легко достичь с помощью Effort. Подробнее о последней технике в [моем блоге] (http://www.vannevel.net/2015/02/26/11/). Я очень не рекомендую пытаться издеваться над всем, что связано с EF, потому что оно превратит ваш код в спагетти. –

ответ

2

Почему мой фиктивный набор пуст?

Поскольку вы не настроили (установки) он предпринимать какие-либо действия при вставке в него данные. Когда создается макет, он «теряет» всю логику, присутствующую в исходном классе, и помечен virtual. Moq просто переопределяет такие методы, чтобы позже вы могли настроить их для выполнения чего-либо (возвращаемое значение, throw и т. Д.).

Естественно, вы можете настроить Add метод для вставки данных обратно в список data блоге:

var mockSet = new Mock<DbSet<Blog>>(); 
mockSet.Setup(m => m.Add<It.IsAny<Blog>()).Callback(blog => data.Add(blog)); 

Когда Add метод будет вызван на ваш DbSet (желательно с помощью вызова службы) сконфигурированной версия Add (Callback метод сообщает Moq «вызывают этот код», когда вызывается метод издевательства).

Вы также можете попытаться предотвратить переопределение Add с использованием параметра CallBase. Однако это может не сработать, так как ваш DbSet не знает о существовании list, вам, скорее всего, придется сконфигурировать его в большей степени.

На заключительной ноте, когда ваш эксперимент кристаллизуется вы должны понимать, что настройка Add не будет необходимости, потому что вы не будете проверять, был ли добавлен блог через GetAllBlogs метод, а скорее проверка на самом издеваться:

mockSet.Verify(m => m.Add(It.IsAny<Blog>()), Times.Once()); 
+0

Интересно ... Полагаю, это имеет смысл, и, возможно, я недопонимаю идею/цель насмешки, как подозревали. Не могли бы вы рассказать о цели насмешки? Кажется, что все, что я делаю, проверяет, действительно ли 'Add' был вызван на объект' Blog'. Возможно, это связано с простотой примера, но это не кажется очень ценным тестом. Я также открыт для ссылок. – Jeff

+0

@Jeff: Ваша идея насмехаться +/- правильная. Что касается теста «GetAllBlogs», то то, что вы делаете, более или менее корректно. Естественно, что уровень детализации во время проверки может быть другим (в конце концов, вы будете проверять объекты, которые вы только что создали, 2 строки выше) - вы можете утверждать, что ссылка на список, количество списков, каждая ссылка на элемент списка и т. Д. 'AddBlog 'с другой стороны (возможно) ничего не возвращает и его« работа »не видна внешнему миру - вот почему вы утверждаете с помощью методов' Verify', что является естественным подходом в таких случаях. –

0

Вы пытались использовать свойство CallBase?

mockSet.CallBase = true;