TDD - 使用属性自动生成代码

时间:2013-03-07 15:06:47

标签: c# visual-studio-2012 tdd code-generation

我正在使用MsTest与RhinoMocks一起练习TDD,我试图像人类一样懒惰,即尽可能使用VS2012自动生成。但是,使用Arrange-Act-Assert方法创建一个完整的测试方法并不总是正确的,只是为了设置我的类及其构造函数和属性。

目前,我发现在我的测试类中创建一些属性是最容易的 - 即使我不使用它们 - 仅仅是为了代码生成。我的问题是,这是一个坏习惯,还有更好/更简单的方法吗?任何评论,无论好坏,都是受欢迎的;谢谢!

[TestClass]
public class MainViewModelTest
{
    private MainViewModel MainViewModel
    {
        get
        {
            var facilityDataEntity = MockRepository.GenerateStub<FacilityDataEntity>();

            var viewModel = new MainViewModel(facilityDataEntity)
            {
                FacilityValue = string.Empty,
                FacilityLabel = string.Empty
            };

            return viewModel;
        }
    }

    private MainViewModel MainViewModelWithFacilityAndShopOrderData
    {
        get
        {
            var facilityDataEntity = MockRepository.GenerateStub<FacilityDataEntity>();
            var shopOrderDataEntity = MockRepository.GenerateStub<ShopOrderDataEntity>();

            var viewModel = new MainViewModel(facilityDataEntity, shopOrderDataEntity)
            {
                FacilityValue = string.Empty,
                FacilityLabel = string.Empty,
                ShopOrder = 99999999,
                RequiredQuantity = 0M,
                ItemCode = string.Empty,
                ItemDescription = string.Empty
            };

            return viewModel;
        }
    }

    [TestMethod]
    public void MainViewModel_TranslateDataEntityListMethodReturnsMainViewModelRecords()
    {
        // Arrange
        var facilityDataEntityList = MockRepository.GenerateStub<IEnumerable<FacilityDataEntity>>();
        var shopOrderDataEntityList = MockRepository.GenerateStub<IEnumerable<ShopOrderDataEntity>>();

        // Act
        IEnumerable<MainViewModel> facilityResults = MainViewModel.TranslateDataEntityList(facilityDataEntityList);
        IEnumerable<MainViewModel> shopOrderResults = MainViewModel.TranslateDataEntityList(facilityDataEntityList, shopOrderDataEntityList);

        // Assert
        Assert.IsInstanceOfType(facilityResults, typeof(IEnumerable<MainViewModel>));
        Assert.IsInstanceOfType(shopOrderResults, typeof(IEnumerable<MainViewModel>));
    }
}

2 个答案:

答案 0 :(得分:3)

在测试类中包含公共代码并没有错,但我会避免在测试之间共享状态。

您可以在这里使用两种方法。

类/测试初始化​​

正如Peter在他的评论中提到的那样,很容易包含初始化方法来为你做这类事情。

//Only runs once per test run
[ClassInitialize]
public void InitClass(){

   //Ideally this should be reserved for expensive operations
   // or for setting properties that are static throughout
   // the lifetime of your test.

}

//Runs for every test
[TestInitialize]
public void TestInit(){

   //Here you can setup common stub/mock behavior
   // that will be common for every test, but ensure
   // it is clean for each test run

}

设置/工厂方法

另一个选择是创建专门的设置或工厂方法,可用于减少重复的测试代码并使测试的目的更清晰。

[TestMethod]
public void ShouldFailIfUserNameIsTed(){

   var user = SetupUserScenario("Ted");

   var result = _myUserService.Validate(user);

   Assert.IsFalse(result);
}

private User SetupUserScenario(String username){

   var user = new User();
   user.Name = username;

   //Do a bunch of other necessary setup

   return user;
}

希望这一切都有道理,但我也提醒你不要对此过于疯狂。如果你在设置方法中加入太多东西,那么你的测试就不那么清楚了。您应该能够阅读测试并找出正在发生的事情,而无需检查代码中的其他一些地方。

答案 1 :(得分:1)

这就是ClassInitialize功能的用途。我会先选择预期和推荐的做事方式。它更容易识别,并且花费更少的时间来理解代码。