XUnit单元测试(实用项目)——看完不会用你打我

这篇具有很好参考价值的文章主要介绍了XUnit单元测试(实用项目)——看完不会用你打我。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、简介

xUnit.net 是针对 .NET 的免费、开源单元测试框架,可并行测试、数据驱动测试。测试项目需要同时引用 xUnit和被测试项目,从而对其进行测试。测试编写完成后,用 Test Runner 来测试项目,Test Runner 可以读取测试代码,并且知道所会使用的测试框架,然后执行,并显示结果。

二、支持平台

xUnit.net 目前支持 .Net Framework、.Net Core、.Net Standard、UWP、Xamarin ,可以在这些平台使用 xUnit 进行测试。

三、核心思想

单元测试的核心思想:万物皆虚拟(mock data)、测试某个类时要假定其他类都正常、单元测试代码和被测试代码的目录结构最好一致。
配置项是虚拟的,配置项各属性的值要重新设置。
类实例是虚拟的,类实例中的不同方法返回什么结果要提前设置。
Http请求是虚拟的,不同http请求返回什么结果要提前设置。

四、XUnit具体用法详解

欲写单元测试首先得有要测试的功能/服务,写服务的过程就不在这里赘述了,写单元测试的时候我会把对应的服务关键代码截图过来让大家对照着看,下面正式开始:

1、准备环境

1.1新建项目

xunit,单元测试,xUnit,UnitTest

xunit,单元测试,xUnit,UnitTest

xunit,单元测试,xUnit,UnitTest

1.2安装依赖项及项目结构概览(FluentAssertions、Moq)

xunit,单元测试,xUnit,UnitTest

2、对AnalyzerService.cs进行单元测试

先来个简单点的对AnalyzerService.cs进行单元测试,具体可参照下述步骤:

  • 创建对应的测试类AnalyzerServiceUnitTest.cs
  • 声明测试对象IAnalyzerService _sut(System Under Test待测系统的缩写)
  • 在构造方法中创建AnalyzerService实例并赋值给测试对象,创建实例所需的参数都要用mock数据,缺哪些就声明哪些
  • 声明并用new对mock数据初始化后还要对其中的属性/方法进行设置才能使用(呼应第三点核心思想,这也是单元测试最重要的一步)
  • 测试对象创建完毕后,要根据IAnalyzerService创建测试方法(单元测试要包含其中的所有方法,甚至更多)
  • 单一方法业务较复杂时可以根据if条件将其拆分成多个测试方法,所以测试方法可能多于接口原有的方法
  • 若原方法有返回值可以根据返回值是否符合预期来进行断言,若原方法没返回值只要不报错即可,若需要判断异常我这也有处理方法
  • 下面直接上代码,我会尽可能的添加注释帮助大家理解

xunit,单元测试,xUnit,UnitTest

namespace LearnUnitTest.Test.Services
{
    public class AnalyzerServiceUnitTest
    {
        private readonly string _token;
        private readonly string _fileMetadataId;
        //声明测试对象
        private readonly IAnalyzerService _sut;
        //声明new AnalyzerService()所需的参数
        private readonly Mock<ISauthService> _sauthService;
        private readonly Mock<IFileMetadataService> _fileMetadataService;
        private readonly Mock<IProcessTimeService> _processTimeService;
        private readonly Mock<IErrorRecordService> _errorRecordService;
        private readonly Mock<IOptions<TimeSettings>> _timeSettings;
        private readonly Mock<IOptions<ReferencingServices>> _referencingServices;
        private readonly Mock<IOptions<ErrorRecordSetting>> _errorRecordSetting;
        private readonly Mock<IActivityService> _activityService;
        private readonly Mock<ILogger<AnalyzerService>> _logger;
        private const string ExceptionMsg = "UT_Exception_Message";

        public AnalyzerServiceUnitTest()
        {
            _token = Guid.NewGuid().ToString();
            _fileMetadataId = "UT_FileMetadataId";
            _sauthService = new Mock<ISauthService>();
            _fileMetadataService = new Mock<IFileMetadataService>();
            _processTimeService = new Mock<IProcessTimeService>();
            _errorRecordService = new Mock<IErrorRecordService>();
            _timeSettings = new Mock<IOptions<TimeSettings>>();
            _referencingServices = new Mock<IOptions<ReferencingServices>>();
            _errorRecordSetting = new Mock<IOptions<ErrorRecordSetting>>();
            _activityService = new Mock<IActivityService>();
            _logger = new Mock<ILogger<AnalyzerService>>();
            //mock数据创建实例后还要根据需求对其中的属性/方法进行设置才能使用
            InitOptions();
            InitServices();

            //创建AnalyzerService实例并赋值给测试对象
            _sut = new AnalyzerService(_sauthService.Object, _fileMetadataService.Object,
                _processTimeService.Object, _errorRecordService.Object,
                _timeSettings.Object, _referencingServices.Object, _errorRecordSetting.Object,
                _activityService.Object, _logger.Object);
        }

        [Fact]
        public async Task Analyze_ShouldSuccess_WhenHasLatestProcessTime()
        {
            LatestProcessTimeRecord latestProcessTimeRecord = new LatestProcessTimeRecord()
            {
                //设置返回值非空
                LatestProcessTime = DateTime.UtcNow
            };
            //AnalyzerService中GetLatestProcessTimeAsync()返回空和非空会进行不同的逻辑处理,所以将其拆分成两个方法
            //通过重写GetLatestProcessTimeAsync()的返回值对不同的情况进行逻辑覆盖
            _processTimeService.Setup(x=>x.GetLatestProcessTimeAsync(It.IsAny<string>()))
                .ReturnsAsync(latestProcessTimeRecord);

            var exception = await Record.ExceptionAsync(async () => await _sut.AnalyzeAsync());
            exception.Should().BeNull();
        }

        [Fact]
        public async Task Analyze_ShouldSuccess_WhenNoLatestProcessTime()
        {
            LatestProcessTimeRecord latestProcessTimeRecord = new LatestProcessTimeRecord()
            {
                //设置返回值为空
                LatestProcessTime = null
            };
            //AnalyzerService中GetLatestProcessTimeAsync()返回空和非空会进行不同的逻辑处理,所以将其拆分成两个方法
            //通过重写GetLatestProcessTimeAsync()的返回值对不同的情况进行逻辑覆盖
            _processTimeService.Setup(x => x.GetLatestProcessTimeAsync(It.IsAny<string>()))
                .ReturnsAsync(latestProcessTimeRecord);

            Exception exception = await Record.ExceptionAsync(async () => await _sut.AnalyzeAsync());
            exception.Should().BeNull();
        }

        [Fact]
        public async Task Analyze_ShouldRecordErrorLog_WhenThrowException()
        {
            LatestProcessTimeRecord latestProcessTimeRecord = new LatestProcessTimeRecord()
            {
                LatestProcessTime = DateTime.UtcNow
            };
            _processTimeService.Setup(x => x.GetLatestProcessTimeAsync(It.IsAny<string>()))
                .ReturnsAsync(latestProcessTimeRecord);
            _fileMetadataService.Setup(x => x.QueryMetadatasAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
                .Callback(() =>
                {
                    throw new Exception(ExceptionMsg);
                });

            await Record.ExceptionAsync(async () => await _sut.AnalyzeAsync());
            //LogLevel参数写法1
            _logger.Verify(x => x.Log(
                It.Is<LogLevel>(logLevel => logLevel == LogLevel.Error),
                It.IsAny<EventId>(),
                It.IsAny<It.IsAnyType>(),
                It.IsAny<Exception>(),
                It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
                Times.Once);
            //LogLevel参数写法2
            _logger.Verify(x => x.Log(
                LogLevel.Error,
                It.IsAny<EventId>(),
                It.IsAny<It.IsAnyType>(),
                It.IsAny<Exception>(),
                It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
                Times.Once);
        }

        [Fact]
        public async Task NoImplement_ShouldThrowException_Method01()
        {
            Func<Task> result = async () => await _sut.NoImplementAsync(_token);
            //链式进行异常判断写法1
            await result.Should().ThrowAsync<NotImplementedException>();
        }

        [Fact]
        public async Task NoImplement_ShouldThrowException_Method02()
        {
            Exception exception = await Record.ExceptionAsync(async () => await _sut.NoImplementAsync(_token));
            //链式进行异常判断写法2
            exception.Should().BeOfType<NotImplementedException>();
        }

        [Fact]
        public async Task NoImplement_ShouldThrowException_Method03()
        {
            try
            {
                //try...catch进行异常判断
                await _sut.NoImplementAsync(_token);
                Assert.True(false);
            }
            catch (NotImplementedException ex)
            {
                Assert.True(true);
            }
            catch
            {
                Assert.True(false);
            }
        }

        [Theory]
        [InlineData(NamingFileType.MaxwellSMLSummary, "EVQ", "maxwellsmlsummary-evq")]
        [InlineData(NamingFileType.MaxwellSMLSummary, "evq", "maxwellsmlsummary-evq")]
        [InlineData(NamingFileType.MaxwellSMLSummary, "EVT", "maxwellsmlsummary-evt")]
        [InlineData(NamingFileType.MaxwellSMLSummary, "evt", "maxwellsmlsummary-evt")]
        [InlineData(NamingFileType.MaxwellSMLSummary, "PROD", "maxwellsmlsummary")]
        [InlineData(NamingFileType.MaxwellSMLSummary, "prod", "maxwellsmlsummary")]
        [InlineData(NamingFileType.MaxwellSMLSummary, "", "maxwellsmlsummary")]
        [InlineData(NamingFileType.General, "evQ", "general-evq")]
        [InlineData(NamingFileType.General, "EvT", "general-evt")]
        [InlineData(NamingFileType.General, "PRod", "general")]
        [InlineData(NamingFileType.General, "", "general")]
        public void GetContainerName_ShouldReturnCorrectContainer(NamingFileType fileType, string envionment, string expect)
        {
            string containerName = fileType.GetContainerName(envionment);
            containerName.Should().BeEquivalentTo(expect);
        }


        private void InitOptions()
        {
            TimeSettings timeSettings = new TimeSettings()
            {
                DefaultUploadTime = "2022-06-01T00:00:00.000Z"
            };
            //IOptions<T>类型的变量通过Value属性来获取实际的参数值,所以这里要重写Value属性
            _timeSettings.Setup(x => x.Value).Returns(timeSettings);

            ReferencingServices referencingServices = new ReferencingServices()
            {
                DataPartitionId = "DataPartition-Id",
                FileServiceURL = "https://global.FileService.URL",
                ActivityServiceURL = "https://global.ActivityService.URL",
                ProcessStatusServiceURL = "https://global.ProcessStatusService.URL"
            };
            _referencingServices.Setup(x => x.Value).Returns(referencingServices);

            ErrorRecordSetting errorRecordSetting = new ErrorRecordSetting()
            {
                DefaultMaxRetryCount = 5
            };
            _errorRecordSetting.Setup(x => x.Value).Returns(errorRecordSetting);
        }

        private void InitServices()
        {
            //AnalyzerService中调用了ISauthService.GetToken(),不重写GetToken()则所有用到此方法的地方返回值都是null(返回值是string类型)
            _sauthService.Setup(x => x.GetToken()).ReturnsAsync(_token);

            List<FileMetadataGetResponse> fileMetadatas = new List<FileMetadataGetResponse>()
            {
                new FileMetadataGetResponse() { Id = _fileMetadataId }
            };
            //当需要重写的方法有参数时,根据参数类型用It.IsAny<T>()代替即可
            _fileMetadataService.Setup(x => x.QueryMetadatasAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
                .ReturnsAsync(fileMetadatas);

            WellTestingEmission wellTestingEmission = new WellTestingEmission()
            {
                Id = _fileMetadataId,
                CreatedTime = DateTime.UtcNow,
                Emissions = new List<EmissionInfo>()
                {
                    new EmissionInfo()
                    {
                        Name = "CO2EstimatedEmissionForGas",
                        Unit = "T",
                        Value = 0.0f
                    }
                }
            };
            _fileMetadataService.Setup(x => x.GetFileContentByMetadataIdAsync(It.IsAny<string>(), It.IsAny<string>()))
                .ReturnsAsync(wellTestingEmission);

            _errorRecordService.Setup(x => x.InsertProcessErrorAsync(It.IsAny<ErrorBaseRecord>(), It.IsAny<string>(), It.IsAny<string>()));

            _errorRecordService.Setup(x => x.UpdateProcessErrorAsync(It.IsAny<ErrorBaseRecord>(), It.IsAny<string>(), It.IsAny<string>()));

            _activityService.Setup(x => x.InsertOrUpdateActivityAsync(It.IsAny<string>(), It.IsAny<WellTestingEmission>(), It.IsAny<OperationalActivity>(), It.IsAny<string>()));
        }
    }
}

3、对ActivityService.cs进行单元测试

本节在AnalyzerServiceUnitTest.cs单元测试的基础上添加了对http请求的处理(直接用System.Net.Http.HttpClient发送Post/Get请求),单元测试不能发送真实的http请求,所以我们要对不同的http请求分别设置对应的返回值(mock数据),具体可参照下述步骤:

  • 创建对应的测试类ActivityServiceUnitTest.cs
  • 声明测试对象IActivityService _sut(System Under Test待测系统的缩写)
  • 在构造方法中创建ActivityService实例并赋值给测试对象,创建实例所需的参数都要用mock数据,缺哪些就声明哪些
  • 声明并用new对mock数据初始化后还要对其中的属性/方法进行设置才能使用(呼应第三点核心思想,这也是单元测试最重要的一步)
  • 测试对象创建完毕后,要根据IActivityService创建测试方法(单元测试要包含其中的所有方法,甚至更多)
  • 单一方法业务较复杂时可以根据if条件将其拆分成多个测试方法,所以测试方法可能多于接口原有的方法
  • 若原方法有返回值可以根据返回值是否符合预期来进行断言,若原方法没返回值只要不报错即可,若需要判断异常我这也有处理方法(参考上一节
  • 下面直接上代码,我会尽可能的添加注释帮助大家理解

xunit,单元测试,xUnit,UnitTest

xunit,单元测试,xUnit,UnitTest

namespace LearnUnitTest.Test.Services
{
    public class ActivityServiceUnitTest
    {
        private readonly string _token;
        private readonly string _fileMetadataId;
        //多次用到的对象要定义成全局变量
        private readonly WellTestingEmission _wellTestingEmission;
        private readonly OperationalActivity _operationalActivity;
        //声明new ActivityService()所需的参数
        private readonly IActivityService _sut;
        private readonly Mock<ISauthService> _sauthService;
        private readonly Mock<IOptions<ReferencingServices>> _referencingServices;
        private readonly Mock<ILogger<ActivityService>> _logger;
        private readonly Mock<IHttpClientWrapperService> _httpClientWrapperSvc;

        public ActivityServiceUnitTest()
        {
            _token = Guid.NewGuid().ToString();
            _fileMetadataId = "UT_FileMetadataId";
            _sauthService = new Mock<ISauthService>();
            _referencingServices = new Mock<IOptions<ReferencingServices>>();
            _logger = new Mock<ILogger<ActivityService>>();
            _httpClientWrapperSvc = new Mock<IHttpClientWrapperService>();
            _wellTestingEmission = new WellTestingEmission()
            {
                JobId = "UT_JobId",
                JobName = "UT_JobName",
                CountryOfOrigin = "UT_CountryOfOrigin",
                CustomerName = "UT_CustomerName",
                FdpNumber = "UT_FdpNumber"
            };
            _operationalActivity = new OperationalActivity()
            {
                Wells = new[] { new Well() { Wellname = "UT_Wellname01", Wellfield = "UT_Wellfield01" } }
            };
            //mock数据创建实例后还要根据需求对其中的属性/方法进行设置才能使用
            InitOptions();
            InitServices();

            _sut = new ActivityService(_sauthService.Object, _httpClientWrapperSvc.Object,
                _referencingServices.Object, _logger.Object);
        }

        [Fact]
        public async Task ExtractActivity_ShouldSuccess()
        {
            var excepted = new ActivityCreateUpdateRequest
            {
                WellInfo = new WellData
                {
                    WellName = _operationalActivity?.Wells[0]?.Wellname,
                    FieldName = _operationalActivity?.Wells[0]?.Wellfield,
                    CountryCode = _wellTestingEmission.CountryOfOrigin,
                    ClientName = _wellTestingEmission.CustomerName
                },
                Execution = new ExecutionData
                {
                    Id = _wellTestingEmission.JobId,
                    Name = _wellTestingEmission.JobName,
                    Time = Convert.ToDateTime(_wellTestingEmission.CreationDate),
                    System = ExecutionSystem.Tallix,
                    Status = ExecutionStatus.Completed
                },
                BusinessContext = new BusinessContextData
                {
                    FDPNumber = _wellTestingEmission.FdpNumber
                },
                Details = new ActivityDetails
                {
                    MetadataIds = new List<string>() { _fileMetadataId }
                }
            };
            RequestBase<ActivityCreateUpdateRequest> result = _sut.ExtractActivity(_fileMetadataId, _wellTestingEmission, _operationalActivity);
            //逐个属性对比属性值是否相同
            excepted.Should().BeEquivalentTo(result.Data);
        }

        [Fact]
        public async Task InsertOrUpdateActivity_ShouldSuccess_WhenInsert()
        {
            ResponseBase<ActivityBatchQueryResponse> activityBatchQueryResponse = new ResponseBase<ActivityBatchQueryResponse>() 
            {
                Data = new ActivityBatchQueryResponse()
                {
                    //TotalCount<=0
                    TotalCount = 0
                }
            };
            ResponseBase<ActivityCreateResponse> activityCreateResponse = new ResponseBase<ActivityCreateResponse>()
            {
                Data = new ActivityCreateResponse() { Id= _wellTestingEmission.JobId }
            };
            //一个方法中调用多个PostAsync/GetAsync时,根据不同的参数类型来进行设置
            _httpClientWrapperSvc.Setup(x => x.PostAsync<RequestBase<ActivityBatchQueryRequest>, ResponseBase<ActivityBatchQueryResponse>>(It.IsAny<string>(), It.IsAny<RequestBase<ActivityBatchQueryRequest>>(), It.IsAny<string>(), It.IsAny<string>()))
                .Returns(Task.FromResult(activityBatchQueryResponse));
            _httpClientWrapperSvc.Setup(x => x.PostAsync<RequestBase<ActivityCreateUpdateRequest>, ResponseBase<ActivityCreateResponse>>(It.IsAny<string>(), It.IsAny<RequestBase<ActivityCreateUpdateRequest>>(), It.IsAny<string>(), It.IsAny<string>()))
                .Returns(Task.FromResult(activityCreateResponse));
            //方法没有返回值时,检查执行过程中不能有异常
            var exception = await Record.ExceptionAsync(async () => await _sut.InsertOrUpdateActivityAsync(_fileMetadataId, _wellTestingEmission, _operationalActivity, _token));
            exception.Should().BeNull();
        }

        [Fact]
        public async Task InsertOrUpdateActivity_ShouldSuccess_WhenInsert_Met401()
        {
            int calls = 0;
            ResponseBase<ActivityBatchQueryResponse> activityBatchQueryResponse = new ResponseBase<ActivityBatchQueryResponse>()
            {
                Data = new ActivityBatchQueryResponse()
                {
                    //TotalCount<=0
                    TotalCount = 0
                }
            };
            ResponseBase<ActivityCreateResponse> activityCreateResponse = new ResponseBase<ActivityCreateResponse>()
            {
                Data = new ActivityCreateResponse() { Id = _wellTestingEmission.JobId }
            };
            _httpClientWrapperSvc.Setup(x => x.PostAsync<RequestBase<ActivityBatchQueryRequest>, ResponseBase<ActivityBatchQueryResponse>>(It.IsAny<string>(), It.IsAny<RequestBase<ActivityBatchQueryRequest>>(), It.IsAny<string>(), It.IsAny<string>()))
                .Returns(Task.FromResult(activityBatchQueryResponse));
            //方法执行过程中主动抛出异常,且仅第一次执行时抛异常,之后就正常执行
            _httpClientWrapperSvc.Setup(x => x.PostAsync<RequestBase<ActivityCreateUpdateRequest>, ResponseBase<ActivityCreateResponse>>(It.IsAny<string>(), It.IsAny<RequestBase<ActivityCreateUpdateRequest>>(), It.IsAny<string>(), It.IsAny<string>()))
                .Returns(Task.FromResult(activityCreateResponse))
                .Callback(() =>
                {
                    calls++;
                    if (calls == 1)
                    {
                        throw new HttpRequestException("", new Exception(), HttpStatusCode.Unauthorized);
                    }
                });
            var exception = await Record.ExceptionAsync(async () => await _sut.InsertOrUpdateActivityAsync(_fileMetadataId, _wellTestingEmission, _operationalActivity, _token));
            exception.Should().BeNull();
        }

        [Fact]
        public async Task InsertOrUpdateActivity_ShouldSuccess_WhenUpdate()
        {
            ResponseBase<ActivityBatchQueryResponse> activityBatchQueryResponse = new ResponseBase<ActivityBatchQueryResponse>()
            {
                Data = new ActivityBatchQueryResponse()
                {
                    //TotalCount>0且Results集合不为空
                    TotalCount = 1,
                    Results = new List<ActivityBatchQueryItem>() { new ActivityBatchQueryItem() { Id = _wellTestingEmission.JobId } }
                }
            };
            _httpClientWrapperSvc.Setup(x => x.PostAsync<RequestBase<ActivityBatchQueryRequest>, ResponseBase<ActivityBatchQueryResponse>>(It.IsAny<string>(), It.IsAny<RequestBase<ActivityBatchQueryRequest>>(), It.IsAny<string>(), It.IsAny<string>()))
                .ReturnsAsync(activityBatchQueryResponse);
            _httpClientWrapperSvc.Setup(x => x.PutAsync<RequestBase<ActivityCreateUpdateRequest>>(It.IsAny<string>(), It.IsAny<RequestBase<ActivityCreateUpdateRequest>>(), It.IsAny<string>(), It.IsAny<string>()))
                .ReturnsAsync(_wellTestingEmission.JobId);
            var exception = await Record.ExceptionAsync(async () => await _sut.InsertOrUpdateActivityAsync(_fileMetadataId, _wellTestingEmission, _operationalActivity, _token));
            exception.Should().BeNull();
        }

        [Fact]
        public async Task InsertOrUpdateActivity_ShouldSuccess_WhenUpdate_Met401()
        {
            int calls = 0;
            ResponseBase<ActivityBatchQueryResponse> activityBatchQueryResponse = new ResponseBase<ActivityBatchQueryResponse>()
            {
                Data = new ActivityBatchQueryResponse()
                {
                    //TotalCount>0且Results集合不为空
                    TotalCount = 1,
                    Results = new List<ActivityBatchQueryItem>() { new ActivityBatchQueryItem() { Id = _wellTestingEmission.JobId } }
                }
            };
            _httpClientWrapperSvc.Setup(x => x.PostAsync<RequestBase<ActivityBatchQueryRequest>, ResponseBase<ActivityBatchQueryResponse>>(It.IsAny<string>(), It.IsAny<RequestBase<ActivityBatchQueryRequest>>(), It.IsAny<string>(), It.IsAny<string>()))
                .ReturnsAsync(activityBatchQueryResponse);
            _httpClientWrapperSvc.Setup(x => x.PutAsync<RequestBase<ActivityCreateUpdateRequest>>(It.IsAny<string>(), It.IsAny<RequestBase<ActivityCreateUpdateRequest>>(), It.IsAny<string>(), It.IsAny<string>()))
                .ReturnsAsync(_wellTestingEmission.JobId)
                .Callback(() =>
                {
                    calls++;
                    if (calls == 1)
                    {
                        throw new HttpRequestException("", new Exception(), HttpStatusCode.Unauthorized);
                    }
                });
            var exception = await Record.ExceptionAsync(async () => await _sut.InsertOrUpdateActivityAsync(_fileMetadataId, _wellTestingEmission, _operationalActivity, _token));
            exception.Should().BeNull();
        }

        private void InitOptions()
        {
            ReferencingServices referencingServices = new ReferencingServices() 
            {
                DataPartitionId = "DataPartition-Id",
                FileServiceURL = "https://global.FileService.URL",
                ActivityServiceURL = "https://global.ActivityService.URL",
                ProcessStatusServiceURL = "https://global.ProcessStatusService.URL"
            };
            //IOptions<T>类型的变量通过Value属性来获取实际的参数值,所以这里要重写Value属性
            _referencingServices.Setup(x => x.Value).Returns(referencingServices);
        }

        private void InitServices()
        {
            //AnalyzerService中调用了ISauthService.GetToken(),不重写GetToken()则所有用到此方法的地方返回值都是null(返回值是string类型)
            _sauthService.Setup(x => x.GetToken()).ReturnsAsync(_token);
        }
    }
}

五、总结

完成上边两个单元测试之后其余Services的处理也都大同小异,这里就不完全展示了。

不同公司对单元测试的定义多多少少会有些不同,这里探讨的是xUnit+Moq数据的方式,在此之前我也看过许多博主关于单元测试的文章,但大多写的比较简单,比如:测试的方法就是个加减乘除,Assert比较一下结果是否正确就结束了,这样的文章很难应用到项目中,我也是后来接触到单元测试才有幸整理出这么一套东西,如果对大家有所帮助请点赞、关注、评论支持下,谢谢。文章来源地址https://www.toymoban.com/news/detail-767723.html

到了这里,关于XUnit单元测试(实用项目)——看完不会用你打我的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C语言指针进阶--超详细教学(不会你打我…)

    C语言中最难之一莫过于指针了,但是你看完我的博客,你会对指针有更深一步的了解。首先,我们都知道,指针就是一个变量,用来存放地址,指针就是地址,地址就是指针,地址是内存中的一块内存空间。 那么什么是指针变量呢? 我们可以用过(取地址符号)取出变量的内

    2024年01月22日
    浏览(38)
  • Web UI 自动化测试方案(超级干货)看完不会你找我

    项目讨论 一、项目中符合自动化测试的部分有哪些?(目标和范围 scope, 准入准出标准) 1、稳定的需求点、变动较少的页面 2、每日构建后的测试验证 daily build 3、比较频繁的回归测试 4、需要在多平台上运行的相同测试案例、组合遍历型的测试、大量的重复任务 二、自动化用

    2024年04月12日
    浏览(43)
  • 史上最全openstack-T版安装,学不会你打我

    系统 CentOS-7-x86_64-Minimal-1908 节点 controller 4C        4G /boot 200MB /swap 2G 剩下的全部给/ compute 4C        4G /boot 200MB /swap 2G 剩下的全部给/ 1.1.初始化环境 1.2.安装时间服务器(双节点) 1.3.安装OpenStack包(双节点) 1.4.安装依赖什么的yum源(双节点) 1.5.安装数据库(controller)

    2024年02月02日
    浏览(40)
  • 工作3年,还不会写单元测试?新技能get(2),帮你突破瓶颈

    比如常见的JavaWeb项目代码中,Controller层,DAO层以及其他仅涉及接口转发相关的方法,往往不需要单测覆盖。而业务逻辑层的各种Service则需要重点测试。 对于自定义的工具类,正则表达式等固定逻辑,也是必须要测试的。因为这部分逻辑一般都是公共且通用的,一旦逻辑错误

    2024年04月14日
    浏览(40)
  • Spring Boot实用技巧之单元测试

      百度百科 :单元测试(unit testing)是指对软件中的最⼩可测试单元进⾏检查和验证的过程   最小可测试单元因人而异,有的人认为是方法,有的人认为是类,作者更偏向于方法的说法   执行单元测试的目的是检测开发者编写的⼀⼩段代码(或功能)是否正确。如果

    2024年02月04日
    浏览(41)
  • Spring Boot进阶(25):文件上传的单元测试怎么写?不会我教你 | 超级详细,建议收藏

            文件上传是现代Web应用程序中常见的功能,因此编写高效的文件上传单元测试是确保应用程序质量的关键步骤之一。但是,很多开发者可能会遇到单元测试速度慢或者不准确的问题,这使得测试变得更加繁琐和无聊。因此,本篇文章将为你提供一些实用技巧和最佳实

    2024年02月08日
    浏览(44)
  • 【Java实用干货】使用@SpringBootTest注解进行单元测试

    【【Java实用干货】使用@SpringBootTest注解进行单元测试 大家好,我是洲洲,欢迎关注,一个爱听周杰伦的程序员。关注公众号【程序员洲洲】即可获得海量学习资料、面试笔记、大厂独家学习体系路线等…还可以加入技术交流群~ @SpringBootTest注解是SpringBoot自1.4.0版本开始引入的

    2024年02月05日
    浏览(45)
  • XUnit数据共享与并行测试

    在单元或者集成测试的过程中,需要测试的用例非常多,如果测试是一条一条过,那么需要花费不少的时间。从 V2 开始,默认情况下 XUnit 自动配置并行(参考资料),大大提升了测试速度。本文将对 ASP.NET CORE WEBAPI 程序进行集成测试,并探讨 XUnit 的数据共享与测试并行的方

    2024年02月03日
    浏览(55)
  • mac录屏软件推荐!相信我,看完你不会后悔

    有粉丝后台问小编,自己的电脑是mac电脑,不知道如何使用mac电脑录屏,有没有mac录屏软件推荐?小编之前也是用的Windows电脑进行录屏,后来换了mac,经过多年的摸索,熟练掌握了录屏方法。今天小编就带大家了解下有关Mac电脑的录屏方法,以及几款好用的录屏软件推荐。

    2024年02月08日
    浏览(45)
  • 软件测试拿到项目之后该怎么做?请仔细看完这篇文章

    学习软件测试最关键的就是项目实战,如果说我们单纯的学了很多的软件测试理论基础或者很多工具和技术的话,但是没有项目实战去演练,那么面试还是被淘汰。 为了解决大家这样的问题,我搭建在自己的阿里云服务器上,其实就和你们企业自己部署在你们自己服务器上完

    2024年02月14日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包