kram005
kram005

Reputation: 13

How to write test using xUnit for service layer?

How do I write a test using xUnit for a service layer? I'm using dependency injection for my service layer, but how can I make an instance of it using an in memory database?

Here is what I got so far: I follow this link and here is what I came up to use my service layer, but it was a mess and I don't know how to simplify it. I am using an interface of the service in all of my controllers.

[Fact]
public void Add_writes_to_database()
{
    var options = new DbContextOptionsBuilder<ApplicationDbContext>()
        .UseInMemoryDatabase(databaseName: "Add_writes_to_database")
        .Options;

    // Run the test against one instance of the context
    using (var context = new ApplicationDbContext(options))
    {
        var productRepo = new Repository<Product>(context);
        var categoryRepo = new Repository<Category>(context);
        var categoryMappingRepo = new Repository<ProductCategoryMapping>(context);
        var categoryService = new CategoryService(context, categoryRepo, categoryMappingRepo);
        var manufacturerRepo = new Repository<Manufacturer>(context);
        var manufacturerMappingRepo = new Repository<ProductManufacturerMapping>(context);
        var manufacturerService = new ManufacturerService(context, manufacturerRepo, manufacturerMappingRepo);
        var imageRepo = new Repository<Image>(context);
        var imageMappingRepo = new Repository<ProductImageMapping>(context);
        var imageService = new ImageManagerService(imageRepo, imageMappingRepo);
        var specificationRepo = new Repository<Specification>(context);
        var specificationMappingRepo = new Repository<ProductSpecificationMapping>(context);
        var specificationService = new SpecificationService(context, specificationRepo, specificationMappingRepo);
        var productService = new ProductService(context, productRepo, categoryService, manufacturerService, imageService, specificationService);

        var product = new Product() { Id = Guid.NewGuid(), Name = "Product1", Price = 100m };

        productService.InsertProduct(product);
    }

    // Use a separate instance of the context to verify correct data was saved to database
    using (var context = new ApplicationDbContext(options))
    {
        var productRepo = new Repository<Product>(context);
        var categoryRepo = new Repository<Category>(context);
        var categoryMappingRepo = new Repository<ProductCategoryMapping>(context);
        var categoryService = new CategoryService(context, categoryRepo, categoryMappingRepo);
        var manufacturerRepo = new Repository<Manufacturer>(context);
        var manufacturerMappingRepo = new Repository<ProductManufacturerMapping>(context);
        var manufacturerService = new ManufacturerService(context, manufacturerRepo, manufacturerMappingRepo);
        var imageRepo = new Repository<Image>(context);
        var imageMappingRepo = new Repository<ProductImageMapping>(context);
        var imageService = new ImageManagerService(imageRepo, imageMappingRepo);
        var specificationRepo = new Repository<Specification>(context);
        var specificationMappingRepo = new Repository<ProductSpecificationMapping>(context);
        var specificationService = new SpecificationService(context, specificationRepo, specificationMappingRepo);
        var productService = new ProductService(context, productRepo, categoryService, manufacturerService, imageService, specificationService);

        Assert.Equal(1, productService.GetAllProduct().Count());
    }
}

My productService has lots of dependencies to other services, repository and context.

Upvotes: 1

Views: 1643

Answers (1)

Marc
Marc

Reputation: 3959

Yes, you have a lot of dependent services and repositories. Since you already use Dependency Injection for your services, I suggest that you make use of an IoC Container. This will not only remove lots of code to setup your integration tests, it will also help you to resolve any service in your application easily.

You could create a class for your type mappings like this:

public class Services
{
    private readonly DbContextOptions _options;
    private readonly IUnityContainer _container;

    public Services(DbContextOptions options)
    {
        _options = options;
        _container = new UnityContainer();
        RegisterTypes();
    }

    private void RegisterTypes()
    {
        _container.RegisterType<IApplicationDbContext, ApplicationDbContext>(new ContainerControlledLifetimeManager(), new InjectionConstructor(_options));
        _container.RegisterType<IProductService, ProductService>(new ContainerControlledLifetimeManager());
        _container.RegisterType<ISpecificationService, SpecificationService>(new ContainerControlledLifetimeManager());
        _container.RegisterType<IImageManagerService, ImageManagerService>(new ContainerControlledLifetimeManager());
        _container.RegisterType<IRepository<ProductSpecificationMapping>, Repository<ProductSpecificationMapping>>(new ContainerControlledLifetimeManager());
        // etc ...
    }

    public T Get<T>()
    {
        return _container.Resolve<T>();
    }
}

Then you are able to minimize the code in your test which is needed to resolve a product service:

[Fact]
public void Add_writes_to_database()
{
    var options = new DbContextOptionsBuilder<ApplicationDbContext>()
        .UseInMemoryDatabase(databaseName: "Add_writes_to_database")
        .Options;

    var services = new Services(options);
    var target = services.Get<IProductService>();

    // to your testing
}

I haven't tested and verified these lines of code in VS, but it should give you an idea. We are using this approach with Unity in one of our application as well, and you can use your favorite IoC Container. You also need Interfaces for your Repositories and Services, but it is better to have them anyway :-)

Upvotes: 1

Related Questions