Base solution for your next web application
Open Closed

Unit Test Project for separate DbContext #9281


User avatar
0
fabiovalerio created

Hi, how can I write a newly Unit Test project for a separate DbContext? I developed some plugins, and I'd like to perform unit testing without modify Tests code-base, adding my own DbContext registrations!

Actually I duplicated "Tests" project with a new name, and replicated logic behind ServiceCollectionRegistrar to register my own DbContext. All seems to be working correctly, but during a test execution, I'm expecting to found in DB an initial data (seeded from TestDataBuilder classes), but nope! Repository.GetAll() returns an empty list!

I know that InMemory DB could not be used by multiple threads, and is acceptable to not find TestData seeded by "Test.Base" project, but I'm expecting to found my own seeded data!

I'm in wrong with this approach? Do you have any suggestion to accomplish this scenario?


6 Answer(s)
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @fabiovalerio

    Do you use the AppTestBase.cs from existing Test project or did you also copy this one ?

  • User Avatar
    0
    fabiovalerio created

    Hi @ismcagdas, I just copied it and replaced what affecting to my plugin namespaces and classes. More, I put my TestModule under Dependency with the auto-generated ANZ TestModule

  • User Avatar
    0
    michael.pear created

    I recently did this in order to allow for using my DB Context with MSSQLocalDb rather than SQLLite or an InMemory database, due to some of the limitations of those. I also had the objective of keeping the project as simple as possible, without all the cpmplexities that come along with the AspNetZero test. I found that modeling it after the ".GraphQL.Tests" was an easier starting point. ( is your project name) Here are the steps I followed: <br>

    1. Created separate DbContext in .EntityFrameWorkCore project, which except for modifications to EntityFrameworkCoreModule.cs to initialize the context.
    2. Create a new, empty project to use for your tests. (suggest in the tests asp-netcore/tests folder). (Will use name below.
    3. Add a Module.cs by copying and modifying .GraphQL.Tests Module.cs Change the DependsOn list to reference EntityFrameworkCoreModule and AbpTestBaseModule. I also added a dependency to my module to a utilities module where I have a dataloader and test date for the database. [I'll post a copy of my module file in a subsequent message rather than describe the details]
    4. Add a custom configuration accessor, so you can access an appsettings.json. I copied and used the "TestAppConfigurationAccessor" class in ".Test.Base. This step may not be needed...I originally intended to use it for a connection string, but ended up hardcoding in my test base class.
    5. Create a TestBase class. This will create the context, create the schema if necessary, and seed the data. Because I am using a persistent SQLServer instance, then I had to allow for seeding data only if the database was empty. Also, I assume that migrations are not automatically done, so I would have to delete the database on any schema changes, which I've not allowed for in the base module.  I'll post a skeleton of this in a subsequent message.
    6. Create a teste case class, and derive the class from your test base class in step 5. Add a [Fact] annotated method, and try accessing the context which was created in your test base class.
    7. At this point, you should be able to run your test and access your separate DB context.
  • User Avatar
    0
    michael.pear created

    Module class from Step 3:

    I replaced my project name with "&lt;YourProjectName>" for your test project. Also, the data loading module reference can be left out if you will seed data directly in your test base class.

    namespace <YourProjectName>
    {
        [DependsOn(
            typeof(<DataLoadingModuleIfYouHaveOne>), //This is a module with data loading utilities
            typeof(NexusAppsEntityFrameworkCoreModule),
            typeof(AbpTestBaseModule))]
        public class <YourProjectName>Module : AbpModule
        {
            public <YourProjectName>Module(<AspNetZeroProject>EntityFrameworkCoreModule dbModule)
            {
                //This keeps core module from setting setting up db context. 
                //Will do that within this class.
                dbModule.SkipDbContextRegistration = true;
            }
            public override void PreInitialize()
            {
                var configuration = GetConfiguration();
                //Use as means to communicate configinfo
                LocalConfiguration = configuration;
                //May not be needed.
                Configuration.Modules.AbpAutoMapper().UseStaticMapper = false;
                Configuration.ReplaceService<IAppConfigurationAccessor, <YourProjectName>ConfigurationAccessor>();
                Configuration.Modules.AspNetZero().LicenseCode = configuration["AbpZeroLicenseCode"];
            }
    
            public IConfigurationRoot LocalConfiguration { get; set; }
    
            public override void Initialize()
            {
                IocManager.RegisterAssemblyByConvention(typeof(<YourProjectName>Module).GetAssembly());
            }
    
            private static IConfigurationRoot GetConfiguration()
            {
                return AppConfigurations.Get(Directory.GetCurrentDirectory(), addUserSecrets: false);
            }
        }
    }
    
  • User Avatar
    0
    michael.pear created

    Test Base Class in Step 5

    Subsitute for or whatever you ended up using for your Module file and Test base class. <br>

    namespace <YourProjectName>
    {
        public class <YourProjectName>Base: <YourProjectName>Base<<YourProjectName>Module>
        {
            string connectionString = "YourConnectionString ";
    
    private YourDbContext _dbContext = null;
            public Boolean IsSeeded { get; set; }
            public <YourProjectName>Base() : base()
            {
                if (_dbContext == null)
                {
                    var builder = new DbContextOptionsBuilder<YourDbContext>();
                    builder.UseSqlServer(connectionString);
                    _dbContext = new YourrDbContext(builder.Options);
                    _dbContext.Database.EnsureCreated(); //Make sure database schema is created
                    //Check to see if the data is seeded already
                    var count = _dbContext.<SomeEntityThatWouldIndicateSeedingAlreadyDone>.Count();
                    if (count >0 )
                    {
                        IsSeeded = true;
                    } else
                    {
                        IsSeeded = false; //Indicate that data seeding is needed
                    }
                }
            }
            public TrainingManagerDbContext GetContext()
            {
                //Check for seeding, and load data if not
                if (!IsSeeded)
                {
    
    //Alternative to external data loader is to build in the seeding operation directly here
                    using (var loader = LocalIocManager.ResolveAsDisposable<YourDataLoader>())
                    {
                        loader.Object.CreateDbContext(connectionString);
                        loader.Object.LoadData(@"data\SeedData.json");
                    }
                    IsSeeded = true;
                }
                //Make sure _dbContext is not disposed during seeding operation!
                return _dbContext;
            }
        }
        public abstract class <YourProjectName>Base<T> : AbpIntegratedTestBase<T> where T : AbpModule
        {
            protected <YourProjectName>Base()
            {
    
            }
        }
    
  • User Avatar
    0
    ismcagdas created
    Support Team

    Thanks a lot @michael.pear for your detailed explanation :)