代码之家  ›  专栏  ›  技术社区  ›  caltuntas

我该如何处理单元测试中太多的模拟期望?

  •  1
  • caltuntas  · 技术社区  · 16 年前

    我正在用MVP模式为我的演示课程编写单元测试。但我在编写模拟设置代码时遇到了困难。

    我有一个演示者,当演示者的Load方法被调用时,我想测试视图应该加载类属性、表字段、数据类型、设置演示者。…因此,当演示者加载时,当我有不同的事情要做时,我必须为测试添加新的期望。而且测试每次都越来越大。

        [Test]
        public void When_Presenter_Loads_View_Should_Display_Selected_Class_Properties()
        {
            IList<string> dataTypes =new List<string>();
            IClassGenerationView view = mockRepository.StrictMock<IClassGenerationView>();
            tableRepository = mockRepository.Stub<ITableRepository>();
    
            using(mockRepository.Record())
            {
                SetupResult.For(tableRepository.GetDataTypes()).Return(dataTypes);
                view.Presenter = null;
                LastCall.IgnoreArguments();
                view.DataTypes = dataTypes;
                view.Show();
    
                view.ClassProperties = classProperties;
                view.TableName = "Table";
                view.Table = table;
                LastCall.IgnoreArguments();
            }
    
    
            using(mockRepository.Playback())
            {
                ClassGenerationPresenter presenter = new ClassGenerationPresenter(view, clazz,  tableRepository);
                presenter.Load();
            }
        }
    

    这段代码中有代码味吗?我如何改进或简化这一点?

    2 回复  |  直到 13 年前
        1
  •  2
  •   Dennis Doomen    13 年前

    多年来,我一直在努力解决这个问题。起初我使用MVP模式,但后来切换到演示模型(类似于WPF/Silverlight的MVVM)。无论如何,结果从来都不令人满意,尤其是在UI变化迅速的敏捷项目中。因此,我们不再为这些类型的类编写测试,而是改用SpecFlow/WaTiN来创建仍然可维护的自动化UI测试。在此处阅读更多信息: http://www.codeproject.com/Articles/82891/BDD-using-SpecFlow-on-ASP-NET-MVC-Application

    如果你仍然想为UI逻辑编写测试,我绝对不会使用设置方法从测试中删除一些东西。您必须能够理解测试的因果关系,而无需上下浏览。相反,使用更BDD风格的单元测试,就像我在这篇博客文章中解释的那样: http://www.dennisdoomen.net/2010/09/getting-more-out-of-unit-testing-in.html

    一般来说,我对本质上非常理性的类使用这些BDD风格的测试,对普通类使用更多的AAA风格的测试。

        2
  •  1
  •   caltuntas    16 年前

    经过漫长的不眠之夜和研究,我找到了这个解决方案。当我仔细思考时,我想到了这个。我在一次测试中测试了太多的行为。我把测试改成了这样

    [TestFixture]
    public class When_Presenter_Loads
    {
        private MockRepository mockRepository;
        private ITableRepository tableRepository;
        private IClass clazz;
        private Dictionary<string, Type> properties;
        private IClassGenerationView view;
        private ClassGenerationPresenter presenter;
    
        [SetUp]
        public void Setup()
        {
            mockRepository =new MockRepository();
            properties = new Dictionary<string, Type>();
    
            clazz = mockRepository.DynamicMock<IClass>();
            view = mockRepository.DynamicMock<IClassGenerationView>();
            tableRepository = mockRepository.Stub<ITableRepository>();
    
    
        }
    
        [Test]
        public void View_Should_Display_Class_Properties()
        {
            using(mockRepository.Record())
            {
                SetupResult.For(clazz.Properties).Return(properties);
                view.ClassProperties = properties;
            }
    
            using(mockRepository.Playback())
            {
                presenter = new ClassGenerationPresenter(view, clazz, tableRepository);
                presenter.Load();
            }
        }
    
        [Test]
        public void View_Should_Display_Class_Name_As_A_Table_Name()
        {
            using (mockRepository.Record())
            {
                SetupResult.For(clazz.Name).Return("ClassName");
                view.TableName = "ClassName";
            }
    
            using (mockRepository.Playback())
            {
                presenter = new ClassGenerationPresenter(view, clazz, tableRepository);
                presenter.Load();
            }
        }
    
        [Test]
        public void View_Should_Display_SQL_Data_Types()
        {
            List<string> dataTypes = new List<string>();
    
            using(mockRepository.Record())
            {
                SetupResult.For(tableRepository.GetDataTypes()).Return(dataTypes);
                view.DataTypes = dataTypes;
            }
    
            using(mockRepository.Playback())
            {
                presenter = new ClassGenerationPresenter(view, clazz, tableRepository);
                presenter.Load();
            }
        }
    
        [Test]
        public void View_Should_Show_Table()
        {
            using (mockRepository.Record())
            {
                SetupResult.For(clazz.Name).Return("ClassName");
                view.Table = null;
                LastCall.IgnoreArguments();
            }
    
            using (mockRepository.Playback())
            {
                presenter = new ClassGenerationPresenter(view, clazz, tableRepository);
                presenter.Load();
            }
        }
    }
    

    我曾多次使用动态模拟来测试一种行为。你也可以阅读Dave的 One Mock Per Test 关于此的文章