Тестирование ядра .Net с помощью Xunit - PullRequest
0 голосов
/ 18 октября 2018

Это первый раз, когда я пишу тестовый пример, и я не застрял и не уверен, что делать дальше.

У меня есть следующий 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, который я добавил.Здесь я использую MOQ, чтобы установить связь с 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);
    }

1 Ответ

0 голосов
/ 19 октября 2018

Дерево выражений не может содержать вызов или вызов, использующий необязательные аргументы

Вы пытаетесь «вызвать» метод ScanAsync для еще не настроенного GetContext ().Чтобы решить эту проблему, вам нужно установить возвращаемое значение для GetContext (), прежде чем пытаться настроить ScanAsync ()


Этот код очень сложно протестировать, поэтому давайте проведем его рефакторинг.

У вас естьизменить прямые вызовы _clientAccessor.GetContext() на внедрение IDynamoDBContext.DynamoDbClientInitialization не имеет смысла, так как может быть легко заменен на IAmazonDynamoDb.Чтобы избавиться от подробных строк кода чтения конфигурации, используйте

services.AddAWSService<IAmazonDynamoDB>();

Все вызовы DynamoDb должны быть заключены в отдельный класс, называемый, например, 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);
        }
    }

Теперь ваш контроллер будет изменен таким образом 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();
        }
    }

Рефакторинг завершен, начните писать модульные тесты 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));
        }
    }

Вот и все, я провел два теста для каждого действия контроллера, потому что он гораздо более понятен - если вам действительно нужен только один тест, вы можете легко присоединиться к ним

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...