使用 Xunit 进行.Net 核心测试

2023-11-29

这是我第一次编写测试用例,我并没有陷入困境,也不知道如何进一步进行。

我有以下 API。在下面的示例中,我有 2 个端点想要执行测试。

public class ValuesController : Controller
{
    //This interface is used to setup dynamo db and connection to aws
    private IDynamoDbClientInitialization _clientAccessor;
    private static string dynamoDbTable = string.Empty; 

    public ValuesController(IOptions<Dictionary<string, string>> appSettings, IDynamoDbClientInitialization clientAccessor)
    {                      
         var vals = appSettings.Value;            
         dynamoDbTable = vals["dynamoDbTable"];
        _clientAccessor = clientAccessor;
    }

    [HttpGet("api/data")]
    public async Task<List<MyModel>> GetAllData(string type, string status)
    {
        List<ScanCondition> conditions = new List<ScanCondition>();
        conditions.Add(new ScanCondition("Type", ScanOperator.Equal, type));
        conditions.Add(new ScanCondition("Status", ScanOperator.Equal, status));
        var response = await _clientAccessor.GetContext().ScanAsync<MyModel>(conditions, AWSHelperMethods.GetDynamoDbOperationConfig(dynamoDbTable)).GetRemainingAsync();
        return results.Select(x => x.UpdatedBy.ToLower()).ToList();
    }

     [HttpPost("api/save")]
    public async Task<IActionResult> SaveData([FromBody] List<MyModel> listData, string input, string name, string type)
    {
       List<MyModel> model = null; 
       foreach (var data in listData)
       {
         //populating data here
          await _clientAccessor.GetContext().SaveAsync(data, AWSHelperMethods.GetDynamoDbOperationConfig(dynamoDbTable));  
       }         

       return Ok();
    }
}       

public class DynamoDbClientInitialization : IDynamoDbClientInitialization
{
    private readonly DynamoDbClientSettings settings;
    private DynamoDBContext _awsContext;

    public DynamoDbClientInitialization(IOptions<DynamoDbClientSettings> options)
    {
        settings = options?.Value;
    }

    public DynamoDBContext GetContext()
    {         
        //Check is context already exists. If not create a new one.
        if(_awsContext != null)
        {
            return _awsContext;
        }
        else
        {                
            var creds = AWSHelperMethods.SetAwsCredentials(settings.Id, settings.Password);
            var dynamoClient = AWSHelperMethods.GetDynamoDbClient(creds, settings.Region);
            _awsContext = AWSHelperMethods.GetDynamoDbContext(dynamoClient);

            return _awsContext;
        }           
    }
}

public static class AWSHelperMethods
{
   public static BasicAWSCredentials SetAwsCredentials(string awsId, string awsPassword)
    {
        var creds = new BasicAWSCredentials(awsId, awsPassword);
        return creds;
    }

    public static AmazonDynamoDBClient GetDynamoDbClient(BasicAWSCredentials creds, RegionEndpoint awsDynamoDbRegion)
    {
        var client = new AmazonDynamoDBClient(creds, awsDynamoDbRegion);
        return client;
    }

    public static DynamoDBContext GetDynamoDbContext(AmazonDynamoDBClient client)
    {
        var context = new DynamoDBContext(client);
        return context;
    }

    public static DynamoDBOperationConfig GetDynamoDbOperationConfig(string dynamoDbTable)
    {
        DynamoDBOperationConfig config = new DynamoDBOperationConfig() { OverrideTableName = dynamoDbTable };
        return config;
    }   
 }

下面是我添加的 xunit 项目。在这里,我使用最小起订量来最小起订量我的 aws 连接和其他连接。问题在下面针对代码的评论中。

public class DataTest
{

    [Fact]
    public void PassingTest()
    {
       //Arrange
       var dynamoDbTable = "someValue";

       //Trying to moq IOptions
       var moqOp = new Mock<IOptions<Dictionary<string, string>>>();

       //Create an instance to hold desired values
       var vals = new Dictionary<string, string>();

       //Set expected value
       vals["dynamoDbTable"] = dynamoDbTable;

       //Setup dependency behavior
       moqOp.Setup(_ => _.Value).Returns(vals);

       //Trying to moq my connection  
       var moqDb = new Mock<IDynamoDbClientInitialization>();

       //Fake data
       List<MyModel> data = new List<MyModel>()
      { 
        //populate as needed
       };

     moqDb
     .Setup(_ => _.GetContext().ScanAsync<MyModel>
     (It.IsAny<List<ScanCondition>>(), AWSHelperMethods.GetDynamoDbOperationConfig(dynamoDbTable)).GetRemainingAsync())
.ReturnsAsync(data);

      ValuesController controller = new ValuesController(moqOp.Object, 
       moqDb.Object);

      var actual =  controller.GetAllData();
    }       
}

上面我收到的错误如下: 表达式树不能包含使用可选参数的调用或调用

这是上线的

.Setup(_ => _.GetContext().ScanAsync<MyModel>
 (It.IsAny<List<ScanCondition>>(), AWSHelperMethods.GetDynamoDbOperationConfig(dynamoDbTable)).GetRemainingAsync())

有谁可以帮忙解决吗?

- -更新 - -

    public interface IDynamoDbManager
    {
        Task<List<T>> GetAsync(IEnumerable<ScanCondition> conditions);

        Task SaveAsync(T item);
    }

表达式树可能不包含使用以下内容的调用或调用 可选参数

您正在尝试在尚未设置 GetContext() 的情况下“调用”方法 ScanAsync。要解决此问题,您必须在尝试设置 ScanAsync() 之前设置 GetContext() 的返回值


这段代码很难测试,所以让我们重构它。

您必须更改直接调用_clientAccessor.GetContext()注入 IDynamoDBContext。 DynamoDbClientInitialization 没有意义,因为可以轻松替换为 IAmazonDynamoDb。要摆脱冗长的配置读取代码行,请使用

services.AddAWSService<IAmazonDynamoDB>();

对 DynamoDb 的所有调用都应封装在一个单独的类中,例如,Dynamo数据库管理器

public class DynamoDbManager<T> : DynamoDBContext, IDynamoDbManager<T> where T : class
    {
        private DynamoDBOperationConfig _config;

        public DynamoDbManager(IAmazonDynamoDB client, string tableName): base(client)
        {
            _config = new DynamoDBOperationConfig()
            {
                OverrideTableName = tableName
            };
        }

        public Task<List<T>> GetAsync(IEnumerable<ScanCondition> conditions)
        {
            return ScanAsync<T>(conditions, _config).GetRemainingAsync();
        }

        public Task SaveAsync(T item)
        {
            return base.SaveAsync(item, _config);
        }
    }

现在你的控制器将被这样改变值控制器

public class ValuesController : Controller
    {
        private readonly IDynamoDbManager<MyModel> _dynamoDbManager;
        //This interface is used to setup dynamo db and connection to aws
        private static string dynamoDbTable = string.Empty;

        public ValuesController(IOptions<Dictionary<string, string>> appSettings, IDynamoDbManager<MyModel> dynamoDbManager)
        {
            _dynamoDbManager = dynamoDbManager;
            var vals = appSettings.Value;
            dynamoDbTable = vals["dynamoDbTable"];
        }

        [HttpGet("api/data")]
        public async Task<IActionResult> GetAllData(string type, string status)
        {
            var conditions = new List<ScanCondition>
            {
                new ScanCondition("Type", ScanOperator.Equal, type),
                new ScanCondition("Status", ScanOperator.Equal, status)
            };
            var result = await _dynamoDbManager.GetAsync(conditions);
            var response = result.Select(_ => _.UpdatedBy.ToLower()).ToList();
            return Ok(response);
        }

        [HttpPost("api/save")]
        public async Task<IActionResult> SaveData([FromBody] List<MyModel> listData, string input, string name, string type)
        {
            foreach (var data in listData)
            {
                //populating data here
                await _dynamoDbManager.SaveAsync(data);
            }
            return Ok();
        }
    }

重构完成,开始编写单元测试值控制器测试

public class ValuesControllerTests
    {
        private Mock<IDynamoDbManager<MyModel>> _dbManager;
        private ValuesController _valuesController;

        public ValuesControllerTests()
        {
            var mockRepository = new MockRepository(MockBehavior.Loose);
            _dbManager = mockRepository.Create<IDynamoDbManager<MyModel>>();
            var options = new OptionsWrapper<Dictionary<string, string>>(new Dictionary<string, string>()
            {
                {"dynamoDbTable", nameof(MyModel) }
            });
            _valuesController = new ValuesController(options, _dbManager.Object);

        }

        [Fact]
        public async Task GetAllData_ShouldSelectUpdateByInLowerCase()
        {
            //
            var searchResult = new List<MyModel>()
            {
                new MyModel() {UpdatedBy = "UpdatedBy1"}
            };
            _dbManager
                .Setup(_ => _.GetAsync(It.Is<List<ScanCondition>>(list => list.Count == 2)))
                .ReturnsAsync(searchResult);

            //

            var result = await _valuesController.GetAllData("typeValue", "statusValue");

            //
            var okResult = result as OkObjectResult;
            Assert.NotNull(okResult);
            var values = okResult.Value as List<string>;
            Assert.Contains("updatedby1", values);
        }

        [Fact]
        public async Task SaveData_ShouldCallSaveForAllRequestedData()
        {
            //
            var listData = new List<MyModel>()
            {
                new MyModel(),
                new MyModel(),
                new MyModel()
            };
            _dbManager
                .Setup(_ => _.SaveAsync(It.IsAny<MyModel>()))
                .Returns(Task.CompletedTask);

            //

            var result = await _valuesController.SaveData(listData, "","", "");

            //
            _dbManager.Verify(_ => _.SaveAsync(It.IsAny<MyModel>()), Times.Exactly(3));
        }
    }

就这样,我对每个控制器操作做了两次测试,因为它更容易理解 - 如果您确实只需要一次测试,您可以轻松加入它们

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 Xunit 进行.Net 核心测试 的相关文章

随机推荐

  • 如何在 CakePHP 中使用 cookie 进行身份验证?

    我正在尝试使用我的域中其他页面设置的 cookie 来验证用户身份 假设我使用 cakephp 编写了 needpassword example com cookie 是由 auth example com 生成的 使用 Perl CGI
  • 使用循环运行 Gulp 任务

    我的 Gulp 任务有问题 我使用一个任务来创建多个带有 gulp mustache 的 html 文件 这样我最后就有两个文件 index de html 和 index en html 我有一个 json 文件 其中包含字符串 一切都很
  • 如何在 gnuplot 创建的 png 文件中添加希腊字符

    我使用的是 FreeBSD 系统的商业服务器 没有 root 帐户 我用我的帐户安装 gnuplot 但是 我无法设置字体 因此 我将字体文件复制到我的主目录中 例如 usr fonts 然后 我在 gnuplot 文件中使用以下脚本 se
  • 刷新J表

    我有一个从 Vector 创建的 JTable 如何刷新 JTable 以显示添加到 Vector 的新数据 当 TableModel 发生更改时 您的 JTable 应该自动更新 我在这里迈出了一步 但我猜测您没有使用自己的 TableM
  • UML 共享聚合是否意味着不存在循环?

    In short 共享聚合似乎只表达了一种简单的关联 它显然是一个 建模安慰剂 没有客观理由使用它 除非在建模者就特定附加语义达成一致的特定上下文中 然而 一些消息来源声称共享聚合将意味着实例聚合图中不存在循环 这将使聚合成为图建模领域的强
  • C#.NET WebService 返回对象

    我正在使用 ASP NET C 创建 Web 服务 我从网络服务发送各种数据类型 因此我使用以下结构 public enum WS ServiceResponseResult Success Failure public class WS
  • Java内存:Runtime.getRuntime().maxMemory()

    Runtime getRuntime maxMemory 显示哪个内存 是幸存者 伊甸园 旧的 永久的吗 它是堆的最大大小 它松散地基于 mx or Xmx命令行参数 但由于我不明白的原因 可以减少 1 5 它似乎并不完全相同 这不包括永久
  • 为什么我的函数在输入值大小中使用 Python 时间限制?

    我正在尝试创建一个测试命令的函数time在Python中 该函数应该采取m n作为参数并计算MODEXP a e p 其中 p 最多是素数生成器2 m第一种情况下的 e 是2 n在第二种情况下是2 n 1 a是小于p的随机正整数 这是我的代
  • 如何从u-boot启动Linux内核?

    我的 Linux 内核镜像uImage在我的U盘里 我想从 U Boot 启动它 还有设备树文件am335x evm dtb在我的U盘里 我所做的如下 U Boot usb start Re start USB USB0 scanning
  • Android - 蓝牙低功耗远程键盘和鼠标

    计划开发一款 Android 应用程序 允许用户通过蓝牙远程控制键盘和鼠标 我见过一些利用用户家庭网络的应用程序 但想了解一些蓝牙结构 这也将是我在 Android 中的第一个项目之一 并将帮助我学习使用 Android SDK 使用低功耗
  • 仅有时显示远程图像

    我维护一个本地 Intranet 站点 其中显示来自 IMDB com 的电影海报图像 直到最近 我只是让一个 perl 脚本下载我需要的图像并将它们保存到本地服务器 但这变成了一个巨大的空间占用 所以我想我可以简单地将我的网站直接指向 I
  • 让其他应用播放背景音乐

    我想添加其他应用程序 例如 Pandora 或 iTunes 的功能 以便在我的 Sprite Kit 游戏打开时继续播放音乐 我该怎么做呢 对于这个问题的宽泛性 我深表歉意 我只是找不到关于此的很多信息 设置你的AVAudioSessio
  • mysql 错误:超出每小时最大连接数

    我在同一域上为 WordPress 网站运行 php 脚本时遇到错误 Could not connect User abc has exceeded the max connections per hour resource current
  • pgAdmin:如何在输出中查看单元格中的完整值

    我有一个查询 它给我输出单元格中的大输出值 但问题是 为了阅读 我需要拖动列并查看值 在一定范围后 值不会显示 而是显示给我 复制该值后 我也无法获取整组值 Question 如何从输出中看到整组值 选项中有一个设置 Max charact
  • 我什么时候应该使用准备好的语句?

    本来我用的是mysql connect and mysql query做事 然后我学习了SQL注入 所以我正在尝试学习如何使用准备好的语句 我了解 PDO 类的准备和执行函数如何有助于防止 SQL 注入 仅当用户输入存储到数据库中时才需要准
  • 在最新的 Ember.js 构建中的 Ember.View 中定义应用程序模板

    我最近升级到了最新的 Ember js 版本 从 GitHub 页面构建 当使用新路由器时 此功能不再起作用吗 App ApplicationView Ember View extend template Ember Handlebars
  • Celery 和 Django - 没有名为“django”的模块

    我使用描述的说明here Python 2 7 和 Celery 3 1 17 在 celery py 中我有 一开始 from future import absolute import import os from celery imp
  • 使用 Ajax 通过模式上传文件

    我想使用 Ajax 通过模式上传文件 我怎样才能做到这一点 我的模式 div class modal fade in div class modal dialog div class modal content div class moda
  • ConnectionString 属性不打印连接字符串

    我正在尝试使用以下脚本获取数据库数据源的连接字符串 Analysis Server New Object Microsoft AnalysisServices Server Analysis Server connect server da
  • 使用 Xunit 进行.Net 核心测试

    这是我第一次编写测试用例 我并没有陷入困境 也不知道如何进一步进行 我有以下 API 在下面的示例中 我有 2 个端点想要执行测试 public class ValuesController Controller This interfac