Base solution for your next web application
Open Closed

Automapper Unit tests with Effort #586


0
jérôme lambert created

Hi,

I try to implement unit test to test my service. I take a look on sample boilerplate and EventCloud but I keep a problem:

AutoMapper.AutoMapperMappingException : Missing type map configuration or unsupported mapping.

Mapping types:
Categories -> CategoriesDto
B2Peer.Categories.Categories -> B2Peer.Categories.Dtos.CategoriesDto

Destination path:
List`1[0]

Source value:
[Categories_5C5B363066C7796CF519C34A20336BC3DC1E95B62494C9F2F2121E0BCC11DEF3 1]

It looks like I have a problem with Effort EF6 and AutoMapper.

All works fine on my website, I have this problem with my unit testing. I set the attribute to AutoMap my Dto

[AutoMapFrom(typeof(Categories))]
    public class CategoriesDto: EntityDto<int>

Here is my categorie service:

public GetCategoriesOutput GetCategories(GetCategoriesInput input)
        {
            var stopwatch = new Stopwatch();
            stopwatch.Start();
            var source = _cacheManager.GetCache("CategoriesCache")
                .Get("All",
                    GetCategoriesFromDatabase) as IEnumerable<CategoriesDto>;
            //var source = _categoriesRepository.GetAll();
            stopwatch.Stop();
            Logger.Info(string.Format("GetCategories Called (Performance:{0})", stopwatch.Elapsed.ToString("g")));
            return new GetCategoriesOutput
            {
                Categories = source.Where(c=> 
                    c.CategorieSecteurId > 0 && c.CategorieNom != "" && 
                    (!input.OnlyActive || c.CategorieSignifiante == "1"))
            };
        }

        [UnitOfWork]
        private IEnumerable<CategoriesDto> GetCategoriesFromDatabase()
        {
            Logger.Info("GetCategoriesFromdatabase");
            return Mapper.Map<List<CategoriesDto>>(_categoriesRepository.GetAllList());
        }

On my test, I fake the connection like the boilerplate sample show:

//Fake DbConnection using Effort!
            LocalIocManager.IocContainer.Register(
                Component.For<DbConnection>()
                    .UsingFactoryMethod(Effort.DbConnectionFactory.CreateTransient)
                    .LifestyleSingleton()
                );

            //Seed initial data
            UsingDbContext(context => new B2PeerInitialDataBuilder().Build(context));

And my test is quite simple:

private readonly ICategoriesAppService _categorieService;


        public CategoriesRepositoryTests()
        {
            _categorieService = LocalIocManager.Resolve<ICategoriesAppService>();

        }

        [Test]
        public void GetAllCategoriesTest()
        {
            var categories = _categorieService.GetCategories(new GetCategoriesInput
            {
                OnlyActive = true
            });

            //UsingDbContext(context =>
            //{
            //    var categorie = context.Categories.FirstOrDefaultAsync();
            //    Assert.That(categorie, Is.Not.Null);

            //    Assert.That(categories.Categories.Count(), Is.GreaterThan(0)); 
            //});
        }

Did I miss something in my code ?

EDIT:

If I debug using

Mapper.GetAllTypeMaps()

It returns 0 mapping.

Maybe the error comes from abp.automapper not fired ??


6 Answer(s)
  • 0
    hikalkan created
    Support Team

    Probably, AbpAutoMapperModule is not loaded. Does your application module DependsOn AbpAutoMapperModule?

  • 0
    jérôme lambert created

    Yes, that's it. My module was not depending on AutoMapperModule so added it make it works.

    Now I'm still blocked with another dependency error :(:

    Can not find a source with name: EntrepriseNotFound
    

    I use the ILocalizationManager to get my localized string but the injected LocalizationManager doesn't have my source because it is configured on AbpWebModule and not at Application level:

    [DependsOn(typeof(B2PeerDataModule), typeof(B2PeerApplicationModule), typeof(B2PeerWebApiModule))]
        public class B2PeerWebModule : AbpModule
        {
            public override void PreInitialize()
            {
                Configuration.Localization.Sources.Add(
                    new ResourceFileLocalizationSource(
                        B2PeerConsts.LocalizationSourceName,
                        Localization.B2Peer.B2Peer.ResourceManager
                    )
                );
    ...
            }
    ....
    

    I try to resolve an instance with this source and LocalizationConfiguration have the 3 sources wanted (Abp, AbpZero and B2Peer) but LocalizationManager still have the only 2 (Abp and AbpZero)

    protected B2PeerTestBase()
            {
                ...
                LocalIocManager.IocContainer.Register(
                    Component.For<ILocalizationConfiguration>()
                    .Instance(LocalizationConfiguration())
                    .IsDefault()
                    .Named("OverridingLocalizationConfiguration"));
                LocalIocManager.IocContainer.Register(
                    Component.For<ILocalizationManager>()
                    .Instance(LocalizationManager())
                    .IsDefault()
                    .Named("OverridingLocalizationManager"));
            }
    
            private ILocalizationManager LocalizationManager()
            {
                return Resolve<ILocalizationManager>();
            }
    
            private ILocalizationConfiguration LocalizationConfiguration()
            {
                var configuration = Resolve<ILocalizationConfiguration>();
                configuration.Sources.Add(
                    new ResourceFileLocalizationSource(
                        B2PeerConsts.LocalizationSourceName,
                        Web.Localization.B2Peer.B2Peer.ResourceManager
                        )
                    );
                return configuration;
            }
    

    Is there an easy way to define ILocalizationManager to use my source in my TestBase ?

    I don't want to test the translation and don't care if event it returns the sourceKey.

  • 0
    hikalkan created
    Support Team

    Hi,

    You can move localization source to the Core project. I did it for module zero template: <a class="postlink" href="https://github.com/aspnetboilerplate/module-zero-template/blob/master/src/AbpCompanyName.AbpProjectName.Core/AbpProjectNameCoreModule.cs#L21">https://github.com/aspnetboilerplate/mo ... ule.cs#L21</a> Don't forget to make xml files embedded resource.

  • 0
    jérôme lambert created

    I try to move the .resx files to Core project and add this on my Core module in PreInitialize:

    Configuration.Localization.Sources.Add(
                    new ResourceFileLocalizationSource(
                        B2PeerConsts.LocalizationSourceName,
                        Localization.B2Peer.B2Peer.ResourceManager
                    )
                );
    

    And now LocalizationManager contains the 3 sources but it doesn't found my key. But I verify and the key is well present in .resx file.

    What do you mean by make resouce XmlEmbedded ? I use a .resx files generate by Visual Studio with a .designer.cs class.

  • 0
    hikalkan created
    Support Team

    Oh, I thought you are using XML files, sorry.

    I think you are using localization wrong. Is your localization source name "EntrepriseNotFound" ? I suppose this is the Key. Check your localizing code (maybe order of params are wrong).

  • 0
    jérôme lambert created

    Hi,

    I inspect the IOCResolver value with my ILocalizationManager and I saw this exception:

    '((System.RuntimeType) ((Abp.Localization.Sources.Resource.ResourceFileLocalizationSource) new System.Collections.Generic.Mscorlib_DictionaryDebugView<string, Abp.Localization.Sources.ILocalizationSource> (((Abp.Localization.LocalizationManager)_localizationManager)._sources) .Items[3].Value).ResourceManager.ResourceSetType).GenericParameterAttributes' threw an exception of type 'System.InvalidOperationException'

    But I don't understand it.