Jako dinozaur piszący ręcznie kod uruchamiany na platformie Power Apps („Low-code, you fool!”) stworzyłem sobie dziś klasę repozytorium, umożliwiającą pobieranie metadanych tabel Microsoft Dataverse. Rozwiązanie wydawało mi się być banalnym w swojej prostocie:
public interface IMetadataRepository { EntityMetadata GetEntityMedatada(string entityName); void Initialize(IOrganizationService service); } public class MetadataRepository : IMetadataRepository { protected IOrganizationService OrganizationService; public void Initialize(IOrganizationService service) { OrganizationService = service; } public EntityMetadata GetEntityMedatada(string entityName) { var retrieveEntityRequest = new RetrieveEntityRequest { EntityFilters = EntityFilters.All, LogicalName = entityName }; var response = (RetrieveEntityResponse)OrganizationService.Execute(retrieveEntityRequest); return response.EntityMetadata; } }
Powyższe repozytorium wykorzystywałem do dynamicznego pobierania informacji nt. podstawowego atrybutu encji oraz dynamicznego ustawiania jego wartości. Wyglądało to mniej-więcej w następujący sposób:
var entityMetadata = _metadataRepository.GetEntityMedatada(entityName); var entity = new Entity(entityName); entity.Attributes.Add(entityMetadata.PrimaryNameAttribute, entityPrimaryAttributeValue); var entityId = _repository.Create(entity);
Problem pojawił się w momencie próby stworzenia testu jednostkowego dla ww. kodu. Okazało się niestety, że wykorzystywana przeze mnie właściwość klasy EntityMetadata jest tylko do odczytu. Próba utworzenia własnego obiektu ww. typu i poustawiania jego atrybutów wewnątrz testu jednostkowego zakończyła się niepowodzeniem. Na szczęścia ratunkiem okazał się mechanizm refleksji, który do spółki z frameworkiem Moq pozwolił utworzyć „zaślepkę” przedstawionego na początku repozytorium.
Mock<IMetadataRepository> metadataRepository = new Mock<IMetadataRepository>(); metadataRepository.Setup(m => m.GetEntityMedatada(It.IsAny<string>())).Returns(() => { var metadata = new EntityMetadata(); typeof(EntityMetadata) .GetProperty("PrimaryNameAttribute") .SetValue(metadata, "pg_name"); return metadata; });
Nie chcę w tym miejscu wchodzić w dyskusję na temat tego, czy wykorzystania refleksji wewnątrz testów jednostkowych jest dobrą praktyką i czy problem, na który napotkałem nie wynika z błędów w architekturze. Zdaję sobie również sprawę, że powyższą zaślepkę dałoby się pewnie zastąpić dedykowaną strukturą wrapperów. Po rozważeniu alternatyw stwierdziłem jednak, że dla mojego przypadku przytoczone powyżej rozwiązanie jest „good enough” 😉.
Wykorzystujcie więc na własną odpowiedzialność.