.Net core testing with Xunit

后端 未结 1 871
鱼传尺愫
鱼传尺愫 2021-01-22 04:34

This is the first time I am writing test case and I am not sort of stuck and not sure how to proceed further.

I have the following API. In the below sample I have 2 endp

相关标签:
1条回答
  • 2021-01-22 04:34

    An expression tree may not contain a call or invocation that uses optional arguments

    You are trying to "call" a method ScanAsync on not already set up GetContext(). To solve this you have to Setup return value for GetContext() before you tried to Setup ScanAsync()


    This code is very hard to test so let's refactor it.

    You have to change direct calls of _clientAccessor.GetContext() to injection of IDynamoDBContext. DynamoDbClientInitialization doesn't make sense as can be easily replaced with IAmazonDynamoDb. To get rid of verbose config reading code lines, use

    services.AddAWSService<IAmazonDynamoDB>();
    

    All calls to DynamoDb should be encapsulated on a separated class called, for example, DynamoDbManager

    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);
            }
        }
    

    Now your controller will be changed in this way ValuesController

    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();
            }
        }
    

    Refactoring is finished, start writing unit tests ValuesControllerTests

    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));
            }
        }
    

    That's all, I did two tests for each controller action because its far more understandable - if you realy need only one test you can easily join them

    0 讨论(0)
提交回复
热议问题