Как смоделировать Elasticsearch NEST IGetMappingResponse для модульного тестирования - PullRequest
0 голосов
/ 27 ноября 2018

Я и мои коллеги создали модуль доступа к данным, который выполняет CRUD и операции поиска в базе данных ElSQL ElasticeSearch (6.2.0).Мы используем NEST (6.2.0), клиент высокого уровня для сопоставления с DSL-запросом Elasticsearch.Модуль был закодирован в C #, в библиотеке классов в целевой среде .NET Standard 2.0.

В некоторых методах необходимо получить сопоставление индексов индексов, хранящихся в ElasticSearch, чтобы информация о полях, такая как полеТипы и свойства текста могут использоваться для создания поисковых запросов к базе данных Elasticsearch.Мы можем сделать это, используя метод GetMapping от Nest (возвращает Nest.IGetMappingResponse).

У нас нет проблем с выполнением кода.Все отлично работаетОднако, когда мы собираемся создать модульный тест для методов, использующих метод GetMapping, мы не можем смоделировать ответ, который он возвращает (IGetMappingResponse).Первоначально я использовал FakeItEasy, чтобы подделать его, а затем вставил некоторые данные, которые мне нужны, в объект.Однако, чтобы объяснить мою проблему немного лучше, я создал класс, который реализует IGetMappingResponse, чтобы высмеивать мой ответ.Проблема возникает, когда я пытаюсь создать экземпляр TypeMappings для свойства Mapping IndexMapping (взамен - для свойства Indices для макета).Я получаю сообщение об ошибке: «1007 *» «TypMappings» не содержит конструктор, который принимает 0 аргументов.

public class MockedGetMappingResponse : IGetMappingResponse
    {
        public IReadOnlyDictionary<IndexName, IndexMappings> Indices
        {
            get
            {
                return new ReadOnlyDictionary<Nest.IndexName, Nest.IndexMappings>(new Dictionary<Nest.IndexName, Nest.IndexMappings>
                {
                    ["statuses"] = new IndexMappings
                    {
                        Mappings = new TypeMappings() //Error happens here!!
                    }                      

                });
            }
            set { }
        }

        public IReadOnlyDictionary<IndexName, IndexMappings> Mappings
        {
            get
            {
                return null;
            }
            set { }
        }

        public void Accept(IMappingVisitor visitor)
        {
            // Just a test
        }

        public bool IsValid
        {
            get
            {
                return true;
            }
        }

        public ServerError ServerError
        {
            get
            {
                return null;
            }
        }

        public Exception OriginalException
        {
            get
            {
                return null;
            }
        }

        public string DebugInformation
        {
            get
            {
                return "";
            }
        }

        public IApiCallDetails ApiCall
        {
            get
            {
                return null;
            }
            set
            {
                //do nothing
            }
        }

        public bool TryGetServerErrorReason(out string reason)
        {
            reason = "";
            return false;
        }
    }      

}

При поиске определений для типа Nest TypeMappings Я не вижу построенного конструктора.Поэтому я предполагаю, что он должен использовать конструктор по умолчанию, без аргументов.Но, видимо, это не так.Мне нужно знать, как делать насмешливые TypeMappings в IGetMappingResponse.Если нет способа создать экземпляр TypeMappings, мне нужно знать, как создать поддельный IGetMappingResponse для моего ожидаемого ответа, чтобы я мог протестировать свой код.

1 Ответ

0 голосов
/ 28 ноября 2018

С помощью Nkosi я обнаружил, что TypeMappings имеет внутренний конструктор.Зная это, я создал экземпляр TypeMappings, используя Reflection.Вот код, который я использовал для создания объекта.

IReadOnlyDictionary<TypeName, TypeMapping> backingDictionary = new ReadOnlyDictionary<TypeName, TypeMapping>(new Dictionary<TypeName, TypeMapping>
        {
            [typeName.Name] = typeMapping

        });           

        Type[] typeMappingsArgs = new Type[] { typeof(IConnectionConfigurationValues), typeof(IReadOnlyDictionary<TypeName, TypeMapping>) };
        object[] typeMappingsInputParams = new object[] { elasticClient.ConnectionSettings, backingDictionary };
        TypeMappings typeMappings = (TypeMappings)typeof(TypeMappings).GetConstructor(
              BindingFlags.NonPublic | BindingFlags.Instance,
              null, typeMappingsArgs, null).Invoke(typeMappingsInputParams);

Вот код для создания моего поддельного IGetMappingResponse.Я использовал FakeItEasy, чтобы вставить некоторую информацию.

private Nest.IGetMappingResponse GetFakeMappingResponse(Nest.IElasticClient elasticClient)
    {
        var fieldName = "fieldName";
        var indexName = "indexName";
        var documentTypeName = "documentTypeName";

        var typeMapping = new TypeMapping();
        var properties = new Properties();
        typeMapping.Properties = properties;

        var property = new TextProperty();
        property.Name = fieldName;            

        PropertyName propertyName = new PropertyName(fieldName);

        typeMapping.Properties.Add(propertyName, property);

        Type[] typeNameArgs = new Type[] { typeof(string) };
        object[] typeNameInputParams = new object[] { documentTypeName };
        TypeName typeName = (TypeName)typeof(TypeName).GetConstructor(
              BindingFlags.NonPublic | BindingFlags.Instance,
              null, typeNameArgs, null).Invoke(typeNameInputParams);


        IReadOnlyDictionary<TypeName, TypeMapping> backingDictionary = new ReadOnlyDictionary<TypeName, TypeMapping>(new Dictionary<TypeName, TypeMapping>
        {
            [typeName.Name] = typeMapping

        });           

        Type[] typeMappingsArgs = new Type[] { typeof(IConnectionConfigurationValues), typeof(IReadOnlyDictionary<TypeName, TypeMapping>) };
        object[] typeMappingsInputParams = new object[] { elasticClient.ConnectionSettings, backingDictionary };
        TypeMappings typeMappings = (TypeMappings)typeof(TypeMappings).GetConstructor(
              BindingFlags.NonPublic | BindingFlags.Instance,
              null, typeMappingsArgs, null).Invoke(typeMappingsInputParams);

        IndexMappings indexMappings = new IndexMappings();
        typeof(IndexMappings).GetProperty("Mappings", BindingFlags.Public | BindingFlags.Instance).SetValue(indexMappings, typeMappings);

        ReadOnlyDictionary<Nest.IndexName, Nest.IndexMappings> indices = new ReadOnlyDictionary<Nest.IndexName, Nest.IndexMappings>(new Dictionary<Nest.IndexName, Nest.IndexMappings>
        {
            [indexName] = indexMappings

        });

        var fakeMappingResponse = A.Fake<IGetMappingResponse>();
        A.CallTo(() => fakeMappingResponse.ServerError).Returns(null);
        A.CallTo(() => fakeMappingResponse.IsValid).Returns(true);
        A.CallTo(() => fakeMappingResponse.Indices).Returns(indices);

        return fakeMappingResponse;
    }
...