【问题标题】:How do I execute an automated test with the option of specifying test type如何使用指定测试类型的选项执行自动化测试
【发布时间】:2015-03-05 12:17:32
【问题描述】:

如何执行自动化测试,可选择将其指定为单元测试或“轻量级集成测试”,而无需两次编写相同的测试,并且只更改它所依赖的接口以使其成为两者之一?

具体来说,我想执行一个测试并将其指定为单元测试或集成测试。 根据我选择的模式,测试应该会生成一个服务接口。

  • 我不想维护两组相同的代码,唯一的区别是接口:
    • 访问外部系统的服务(集成测试)
    • MockService(单元测试)

示例: Construct testable business layer logic

【问题讨论】:

  • 你可以有两个测试调用一些通用代码。每个单独的测试都会设置依赖项(模拟或其他)。
  • 我建议为单元测试和集成测试设置单独的项目。将两者结合起来会使仅在 IDE 中运行单元测试变得更加困难,给 CI 构建增加了一些复杂性,并且很容易将集成测试错误地标记为单元测试。
  • @David:我的目标是我的“单元测试”(速度)的业务逻辑和我的集成测试的外部依赖项(数据库)。我看不到单元测试类的商业价值,因为它们将通过它们的接口进行重复测试。如果我确实按照您的描述执行单元测试,那么我可能不得不维护大量与接口测试重叠的测试。但是,我确实看到了将视图模型作为主要界面进行测试的价值。我将考虑提取测试逻辑并按照 Matthew 的建议传入依赖项。

标签: c# .net unit-testing automated-tests integration-testing


【解决方案1】:

我不同意 C Bauer。这里完全没有共识。模拟和依赖注入对解决这个问题大有帮助。在过去的几年里,我看到这种方法使用得更频繁,而且效果很好。

通常在角色跨职能的敏捷环境中。一些团队想要一个单一的代码库/解决方案来工作。特别是在代码库的大小相对较小的情况下,让“单元”测试能够作为轻量级集成测试工作得很好。这里没有非黑即白的解决方案,只有最适合手头问题的解决方案。不管其他人怎么说,有多种方法可以解决这个问题,并且解决方案/方法一直在增长和变化。

【讨论】:

    【解决方案2】:

    向应用配置添加一个条目。

    应用配置:

      <appSettings>
        <add key="IsUnitTest" value="True" />
      </appSettings>
    

    然后从配置文件中获取键/值对,并根据配置值设置你的服务依赖。

    测试

    [TestClass]
    public class MyTest
    {
        IServices _service = null;
    
        [TestInitialize]
        public void Setup()
        {
            var isUnitTest = bool.Parse(ConfigurationManager.AppSettings["IsUnitTest"]);
    
            if (isUnitTest)
            {
                _service = new MockService();
            }
            else
            {
                _service = new Service();
            }
        }
    

    。 . .

    【讨论】:

    • 对于遇到此解决方案的其他任何人:artofunittesting.com/unit-testing-review-guidelines 此解决方案几乎违反了计算机科学领域每个人建立的所有测试规则。链接中的示例: 1. 确保测试不包含逻辑或动态值(配置管理器 bool frobnicating); 2. 确保单元测试与集成测试分开(这是为了切换单元/集成测试); 3. 确保测试相互隔离且可重复(这设置了某种用于测试的字段)
    • 作为开发人员和 SDET,此方法解决了我的问题。我现在可以执行相同的逻辑,并且只需按一下开关,就可以让该逻辑到达实际数据库或只是让它到达模拟数据库。请记住,这是一种使我不必重复测试并维护更大代码库的方法。现在,我可以编写一个脚本在单元测试模式和集成模式下执行。
    • 没关系。但是从头开始开发 TDD 并撰写有关最佳实践的书籍的人不同意您所做的一切以及为什么要这样做。我的意思是,每次执行此逻辑时,您实际上都违反了 3 个最佳实践。这对你来说没关系,但我希望没有人认为这是一个明智的决定。
    • 我理解您的担忧。您必须承认,尽管我通过此实现获得了最好的“物有所值”。是的,你是绝对正确的:详细的集成测试应该分开,并且应该彻底检查外部系统状态和行为。可以稍后安排。再次感谢您的洞察力。
    • 相信我,我也更愿意按照你的方式去做!我将堆栈溢出视为可能有类似问题的未来用户的知识库,并且我知道开发 BP 是因为在“简单实现”中发现了问题。我不是想对你竖起大拇指,而是提供由共识产生的观点。
    【解决方案3】:

    好的,我想我明白现在发生了什么。

    您希望能够在运行时更改接口的实现,以更改单元测试运行的位置。

    在这种情况下,您需要某种抽象工厂模式。

    例子:

    public class ViewModel {
        IService _service;
        public ViewModel(IServiceFactory factory){
            _service = factory.Create();
        }
        public void SaveWarehouse(Warehouse aWarehouse) {
            _service.AddWarehouse(aWarehouse);
        }
    }
    public interface IServiceFactory {
        IService Create();
    }
    public class ProductionFactory : IServiceFactory { //Dependency injected
        public IService Create() {
            return new ProdService();
        }
    }
    

    单元测试:

    public class ViewModelTest {
       public void when_adding_warehouse() {
           //Arrange
            var aWarehouse = new WarehouseViewModel { id=1 };
    
            var serviceFactory = new Mock<IServiceFactory>().Object);
            var service = new Mock<IService>();
            serviceFactory.Setup(factory => factory.Create()).Returns(service.Object);
    
            var viewModel = new ViewModel(serviceFactory.Object);
    
            //Act
            viewModel.AddWarehouseCommand(warehouseViewModel);
    
            //Assert
            service.Verify(s=>s.AddNewWarehouse(aWarehouse), Times.Once);
       }
    }
    

    集成测试:

    您的集成测试将包括一个本地内部 IService 实现和一个返回本地 IService 实现的本地内部 IServiceFactory 实现。您的所有测试都将运行得非常好,并且您可以非常轻松地控制数据的去向。

    【讨论】:

      【解决方案4】:

      进行形态测试是没有意义的。

      单元测试测试单个代码独立工作。

      集成测试您的代码在集成到更大的代码库时是否可以正常工作。

      例如,用于单元测试视图模型的验收标准和伪代码:

      public TestMeViewModelTests {
          public when_adding_a_warehouse_then_should_call_service_AddNewWarehouse_given_WarehouseModel {
              //Arrange
              var warehouseViewModel = new WarehouseViewModel { id=1 };
      
              var service = new Mock<IService>();
      
              var interfaceViewModel = new TestMeViewModel(service.Object);
      
              //Act
              interfaceViewModel.AddWarehouseCommand(warehouseViewModel);
      
              //Assert
              service.Verify(s=>s.AddNewWarehouse(wareHouseViewModel), Times.Once);
          }
      }
      

      你看,问题没有交叉授粉。您只是在测试添加新仓库时是否调用了幂等操作。如果您使用的是 ORM,那么您还需要进行单元测试来验证数据服务调用是否正在发生。

      如果您要进行集成测试,那么您的测试项目将指向反映生产的“WarehouseTest”连接字符串,并且您的集成测试可能会执行相同的逻辑,但随后检查以确保仓库由测试插入实际上是在测试结束时在您的数据库中。

      【讨论】:

      • 查看我对最初问题的评论。同样,我的目标是单元测试的速度。
      • 当你模拟一个接口时,你并不是在测试底层类型。您只是在测试,在理想情况下,一切都运行良好,您的代码是否符合您的预期。
      • 所以根据你的回答,我不应该对视图模型进行单元测试?
      • 我的视图模型调用服务。该服务击中模型,而模型又击中数据访问层。结果,我的视图模型将业务逻辑的任务委托给服务接口,然后服务接口可以访问模型和数据库,也可以作为模拟对象,仅使用模拟数据访问模型。
      • 当命令被调用时,让视图模型调用服务是完全可以接受的。让视图模型调用服务不是业务逻辑。服务向模型发送请求,业务逻辑在模型本身内执行。为什么你认为视图模型不能进行单元测试?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-10-22
      • 2021-07-13
      • 2023-03-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-25
      相关资源
      最近更新 更多