C# 如何使用指定测试类型的选项执行自动测试
我如何执行一个自动测试,并选择将其指定为单元测试或“轻量级集成测试”,而不编写两次相同的测试,并且只更改它所依赖的接口,使其成为这两种测试之一 具体来说,我希望执行一个测试,并将其指定为单元测试或集成测试。 根据我选择的模式,测试应该生成一个服务接口C# 如何使用指定测试类型的选项执行自动测试,c#,.net,unit-testing,automated-tests,integration-testing,C#,.net,Unit Testing,Automated Tests,Integration Testing,我如何执行一个自动测试,并选择将其指定为单元测试或“轻量级集成测试”,而不编写两次相同的测试,并且只更改它所依赖的接口,使其成为这两种测试之一 具体来说,我希望执行一个测试,并将其指定为单元测试或集成测试。 根据我选择的模式,测试应该生成一个服务接口 我不想维护两组相同的代码,唯一的区别是接口: 访问外部系统的服务(集成测试) 模拟服务(单元测试) 示例: 进行变形测试是没有意义的 单元测试测试单个代码是否独立工作 集成测试将集成到更大的代码库中时代码是否工作 例如,viewmodel单
- 我不想维护两组相同的代码,唯一的区别是接口:
- 访问外部系统的服务(集成测试)
- 模拟服务(单元测试)
进行变形测试是没有意义的 单元测试测试单个代码是否独立工作 集成测试将集成到更大的代码库中时代码是否工作 例如,viewmodel单元测试的验收标准和psuedocode:
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);
}
}
公共测试MeViewModelTests{
公共当添加仓库时,则应调用服务添加新仓库给定仓库模型{
//安排
var warehouseViewModel=新warehouseViewModel{id=1};
var service=newmock();
var interfaceeviewmodel=新的TestMeViewModel(service.Object);
//表演
interfaceViewModel.AddWarehouseCommand(warehouseViewModel);
//断言
验证(s=>s.AddNewWarehouse(wareHouseViewModel),次.次);
}
}
看,没有异花授粉的担忧。您只是在测试添加新仓库时是否调用了幂等运算。如果您使用的是ORM,那么您还需要进行单元测试来验证是否正在发生dataservice调用
如果您要进行集成测试,那么您的测试项目将指向镜像生产的“WarehouseTest”连接字符串,并且您的集成测试可能会执行相同的逻辑,但随后检查以确保测试插入的仓库在测试结束时实际位于您的数据库中。好的,我想我知道现在发生了什么 您希望能够在运行时更改接口的实现,以便更改单元测试运行的位置 在这种情况下,您需要某种抽象工厂模式 例如:
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);
}
}
公共类ViewModelTest{
添加仓库时公共作废(){
//安排
var aWarehouse=新仓库视图模型{id=1};
var serviceFactory=new Mock().Object);
var service=newmock();
serviceFactory.Setup(factory=>factory.Create()).Returns(service.Object);
var viewModel=新的viewModel(serviceFactory.Object);
//表演
viewModel.AddWarehouseCommand(warehouseViewModel);
//断言
服务.验证(s=>s.AddNewWarehouse(aWarehouse),次.次);
}
}
集成测试:
集成测试将包括本地内部iSeries设备实现和返回本地iSeries设备实现的本地内部iSeries设备实现。您的所有测试都将运行良好,您可以非常轻松地控制数据的去向。在应用程序配置中添加一个条目 应用程序配置:
<appSettings>
<add key="IsUnitTest" value="True" />
</appSettings>
。
.
.我不同意C鲍尔的观点。这里根本没有共识。mock和依赖注入对于解决这个问题有很大的帮助。在过去的几年里,我看到这种方法被更频繁地使用,而且效果很好
通常在角色是跨职能的敏捷环境中。有些团队希望使用单一的代码库/解决方案。特别是在代码库的大小相对较小的情况下,让“单元”测试能够充当轻量级集成测试就可以了。这里没有黑与白的解决方案,只有最适合解决当前问题的方案。不管别人怎么说,解决这个问题有多种方法,而且解决方案/方法一直在增长和变化。您可以有两个调用一些通用代码的测试。每个单独的测试都会设置依赖项(模拟或其他)。我建议为单元测试和集成测试设置单独的项目。将两者结合在一起会使仅在IDE中运行单元测试变得更加困难,给CI构建增加了一些复杂性,并且容易错误地将集成测试标记为单元测试。@David:我的“单元测试”的目标是业务逻辑(速度),而集成测试的目标是外部依赖项(数据库)。我看不到单元测试类有多少业务价值,这些类将通过接口复制测试。如果我按照您描述的那样执行单元测试,那么我就有可能不得不维护大量与接口测试重叠的测试。然而,我确实看到了将视图模型作为主要接口进行测试的价值。我会考虑提取出测试逻辑,然后像马修建议的那样传递依赖关系。请看我对初始问题的评论。同样,我的目标是单元测试的速度。当你模拟一个接口时,你不是在测试底层类型。你只是在测试,在理想的环境下,一切都很完美,你的代码达到了你期望的效果。所以根据你的回答,我不应该对视图模型进行单元测试?我的视图模型调用服务。该服务命中模型,模型又命中数据访问层。因此,我的viewmodel将业务逻辑任务委托给服务接口,然后服务接口可以命中模型和数据库,也可以充当模拟接口,只命中具有模拟数据的模型。在调用命令时,让视图模型调用服务是完全可以接受的。让viewmodel调用服务不是业务逻辑。
[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();
}
}