简体   繁体   English

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

[英]TDD - Using properties to auto-generate code

I am practicing TDD using MsTest together with RhinoMocks, and I am trying to be as lazy as humanly possible, ie make use of VS2012 auto-generation wherever I can. 我正在与RhinoMocks一起使用MsTest来练习TDD,并且我试图尽可能地懒惰,即,尽可能使用VS2012自动生成。 But it doesn't always feel right to create an entire test method with the Arrange-Act-Assert methodology, just to set up my class and its constructors and properties. 但是用Arrange-Act-Assert方法创建整个测试方法并不总是正确的,只是设置我的类及其构造函数和属性。

Currently, I find it easiest to create some properties in my test class - even if I don't use them - solely for the purpose of code generation. 目前,我发现在测试类中创建某些属性(即使我不使用它们)最简单,仅出于代码生成的目的。 My question is, is this a bad habit, and is there a better/easier way to do it? 我的问题是,这是一个坏习惯吗,有没有更好/更容易的方法呢? Any commentary, good or bad is welcome; 欢迎任何评论,无论好坏; Thank you! 谢谢!

[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>));
    }
}

It's not wrong to wrap up common code within your test classes, but I would avoid potentially sharing state between your tests. 在测试类中包装通用代码是没有错的,但是我会避免潜在地在测试之间共享状态。

There are two approaches you can use here. 您可以在此处使用两种方法。

Class/Test Initialization 类/测试初始化

As Peter mentions in his comments, it's easy enough to include initialization methods to do this sort of stuff for you. 正如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

}

Setup/Factory Methods 设置/出厂方法

Another option is to create specialized setup or factory methods that can be used to reduce repeated test code and make the intent of your test clearer. 另一个选择是创建专门的设置或工厂方法,这些方法可用于减少重复的测试代码并使您的测试意图更加清晰。

[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;
}

Hopefully this all makes sense, but I would also caution you not to go too crazy with this. 希望这一切都是有道理的,但我也要提醒您不要为此太疯狂。 If you put too much stuff into setup methods, then your tests will be less clear. 如果您在设置方法中投入了太多的精力,那么您的测试将变得不清楚。 You should be able to read the test and figure out what is going on without having to inspect a bunch of other places in the code. 您应该能够阅读测试并弄清楚发生了什么,而不必检查代码中的其他地方。

That's what the ClassInitialize functionality is for. 这就是ClassInitialize功能的用途。 I would choose expected and recommended means of doing something before anything else. 我会选择期望的和推荐的做某事的方式。 It's more easily recognizable and takes less time to grok the code. 它更容易识别,花更少的时间处理代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM