2013-06-16 6 views
1

Я просто смотрю в TDD с MVC 4.Должен ли я извлечь часть моего тестового кода модуля?

У меня есть orderscontroller, который принимает единицу работы интерфейса в конструкторе:

public OrdersController(IUnitOfWork db) 
{ 
this.db = db;   
} 

// 
// GET: /Orders/ 
public ActionResult Index() 
{      
    return View(db.Orders.GetAll()); 
} 

У меня есть несколько тестов для этого индекса() ,

[TestClass] 
    public class when_the_order_controller_index_action_executes 
    { 
     [TestMethod] 
     public void it_should_render_the_default_view() 
     { 
      var uow = new Mock<IUnitOfWork>(); 
      var db = uow.Object; 

      var orders = new List<Order>() 
      { 
       new Order{CreatedDate = DateTime.Now.AddMonths(-3),OrderID = Guid.NewGuid()}, 
       new Order{CreatedDate = DateTime.Now,OrderID = Guid.NewGuid()} 
      }; 

      uow.Setup(r => r.Orders.GetAll()) 
       .Returns(orders); 

      //arrange 
      var controller = new OrdersController(db); 

      //act 
      var result = controller.Index() as ViewResult; 

      //assert 
      Assert.AreEqual("", result.ViewName); 
     } 

     [TestMethod] 
     public void it_should_pass_orders_as_the_model() 
     { 
      var uow = new Mock<IUnitOfWork>(); 
      var db = uow.Object; 

      var orders = new List<Order>() 
      { 
       new Order{CreatedDate = DateTime.Now.AddMonths(-3),OrderID = Guid.NewGuid()}, 
       new Order{CreatedDate = DateTime.Now,OrderID = Guid.NewGuid()} 
      }; 


      uow.Setup(r => r.Orders.GetAll()) 
       .Returns(orders); 

      //arrange 
      var controller = new OrdersController(db); 

      //act 
      var model = ((ViewResult)controller.Index()).ViewData.Model as IEnumerable<Order>; 

      //assert 
      Assert.IsTrue(orders.Equals(model)); 
     } 
    } 

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

Это хороший/плохой практикой иметь скажем повторно Полезная чтобы вернуть этот список, который может вызвать оба теста?

Любые общие рекомендации по тестам, которые я написал, будут оценены также, поскольку я только сейчас обнимаю любовь tdd!

+0

Я только учусь TDD в данный момент, а я просто смотрю на ваш код и интересно, если я пропустил концепцию с насмешкой/тестированием Почему вы столкнулись с проблемой фактического помещения данных в ваш OUW.db макет в вашем первом тесте? Поскольку все, что вам нужно, чтобы тест был установлен как модель, является нулевым, потому что все, что вы тестируете в этом первом методе, - это то, что представление индекса было возвращено вам; вы затем проверяете, что действие индекса правильно задает модель во втором тесте. Не пытаться быть умным вообще, просто действительно интересно, если я пропустил какую-то концепцию? –

ответ

3

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

Так что в вашем конкретном примере:

[TestClass] 
public class when_the_order_controller_index_action_executes 
{ 
    private IUnitOfWork db; 
    private OrdersController sut; 

    [TestInitialize] 
    public void TestInitialize() 
    { 
     var uow = new Mock<IUnitOfWork>(); 
     this.db = uow.Object; 

     var orders = new List<Order>() 
     { 
      new Order{ CreatedDate = DateTime.Now.AddMonths(-3),OrderID = Guid.NewGuid() }, 
      new Order{ CreatedDate = DateTime.Now,OrderID = Guid.NewGuid() } 
     }; 

     uow.Setup(r => r.Orders.GetAll()).Returns(orders); 

     this.sut = new OrdersController(db); 
    } 

    [TestMethod] 
    public void it_should_render_the_default_view() 
    { 
     //act 
     var result = this.sut.Index() as ViewResult; 

     //assert 
     Assert.AreEqual("", result.ViewName); 
    } 

    [TestMethod] 
    public void it_should_pass_orders_as_the_model() 
    { 
     //act 
     var model = ((ViewResult)this.sut.Index()).ViewData.Model as IEnumerable<Order>; 

     //assert 
     Assert.IsTrue(orders.Equals(model)); 
    } 
} 

Но обычно uow.Setup(r => r.Orders.GetAll()).Returns(orders); является частью //arrange фазы каждого испытания блока, как он отличается. это то, где вы определяете ожидания. В вашем конкретном примере я бы также объединить 2 теста в один:

[TestClass] 
public class when_the_order_controller_index_action_executes 
{ 
    private IUnitOfWork db; 
    private OrdersController sut; 

    [TestInitialize] 
    public void TestInitialize() 
    { 
     var uow = new Mock<IUnitOfWork>(); 
     this.db = uow.Object; 

     var orders = new List<Order>() 
     { 
      new Order{ CreatedDate = DateTime.Now.AddMonths(-3),OrderID = Guid.NewGuid() }, 
      new Order{ CreatedDate = DateTime.Now,OrderID = Guid.NewGuid() } 
     }; 

     uow.Setup(r => r.Orders.GetAll()).Returns(orders); 

     this.sut = new OrdersController(db); 
    } 

    [TestMethod] 
    public void it_should_render_the_default_view_and_pass_the_expected_view_model_to_it() 
    { 
     //act 
     var actual = this.sut.Index(); 

     //assert 
     Assert.IsInstanceOfType(actual, typeof(ViewModel)); 
     var viewResult = (ViewResult)actual; 
     Assert.AreEqual(model, viewResult.Model); 
    } 
} 
+0

Спасибо за подробный ответ. Разве это не лучшая практика иметь один утверждать на тест? – Simon

+1

@Simon: «одно утверждение» следует понимать как «одно логическое понятие», а не одну строку кода с «Assert». –

+0

Обратите внимание, что для некоторых библиотек тестирования, таких как xUnit.net, код инициализации находится в конструкторе тестового класса, в то время как очистка выполняется за счет того, что тестовый класс реализует IDisposable. Кстати, вам также следует попробовать в библиотеке FluentAssertions – MikeSW

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