How to Unit Test Startup.cs in .NET Core - c#

How do people go about Unit Testing their Startup.cs classes in a .NET Core 2 application? All of the functionality seems to be provided by Static extensions methods which aren't mockable?
If you take this ConfigureServices method for example:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<BlogContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddMvc();
}
How can I write tests to ensure that AddDbContext(...) & AddMvc() are called - the choice of implementing all of this functionality via extension methods seems to have made it untestable?

Well yes, if you want to check the fact that extension method AddDbContext was called on services you are in trouble.
The good thing is that you shouldn't actually check exactly this fact.
Startup class is an application composition root. And when testing a composition root you want to check that it actually registers all dependencies required for instantiation of the root objects (controllers in the case of ASP.NET Core application).
Say you have following controller:
public class TestController : Controller
{
public TestController(ISomeDependency dependency)
{
}
}
You could try checking whether Startup has registered the type for ISomeDependency. But implementation of ISomeDependency could also require some other dependencies that you should check.
Eventually you end up with a test that has tons of checks for different dependencies but it does not actually guarantee that object resolution will not throw missing dependency exception. There is not too much value in such a test.
An approach that works well for me when testing a composition root is to use real dependency injection container. Then I call a composition root on it and assert that resolution of the root object does not throw.
It could not be considered as pure Unit Test because we use other non-stubbed class. But such tests, unlike other integration tests, are fast and stable. And most important they bring the value of valid check for correct dependencies registration. If such test passes you could be sure that object will also be correctly instantiated in the product.
Here is a sample of such test:
[TestMethod]
public void ConfigureServices_RegistersDependenciesCorrectly()
{
// Arrange
// Setting up the stuff required for Configuration.GetConnectionString("DefaultConnection")
Mock<IConfigurationSection> configurationSectionStub = new Mock<IConfigurationSection>();
configurationSectionStub.Setup(x => x["DefaultConnection"]).Returns("TestConnectionString");
Mock<Microsoft.Extensions.Configuration.IConfiguration> configurationStub = new Mock<Microsoft.Extensions.Configuration.IConfiguration>();
configurationStub.Setup(x => x.GetSection("ConnectionStrings")).Returns(configurationSectionStub.Object);
IServiceCollection services = new ServiceCollection();
var target = new Startup(configurationStub.Object);
// Act
target.ConfigureServices(services);
// Mimic internal asp.net core logic.
services.AddTransient<TestController>();
// Assert
var serviceProvider = services.BuildServiceProvider();
var controller = serviceProvider.GetService<TestController>();
Assert.IsNotNull(controller);
}

I also had a similar problem, but managed to get around that by using the WebHost in AspNetCore and essentially re-creating what program.cs does, and then Asserting that all of my services exist and are not null. You could go a step further and execute specific extensions for IServices with .ConfigureServices or actually perform operations with the services you created to make sure they were constructed properly.
One key, is I created a unit test startup class that inherits from the startup class I'm testing so that I don't have to worry about separate assemblies. You could use composition if you prefer to not use inheritance.
[TestClass]
public class StartupTests
{
[TestMethod]
public void StartupTest()
{
var webHost = Microsoft.AspNetCore.WebHost.CreateDefaultBuilder().UseStartup<Startup>().Build();
Assert.IsNotNull(webHost);
Assert.IsNotNull(webHost.Services.GetRequiredService<IService1>());
Assert.IsNotNull(webHost.Services.GetRequiredService<IService2>());
}
}
public class Startup : MyStartup
{
public Startup(IConfiguration config) : base(config) { }
}

This approach works, and uses the real MVC pipeline, as things should only be mocked if you need to change how they work.
public void AddTransactionLoggingCreatesConnection()
{
var servCollection = new ServiceCollection();
//Add any injection stuff you need here
//servCollection.AddSingleton(logger.Object);
//Setup the MVC builder thats needed
IMvcBuilder mvcBuilder = new MvcBuilder(servCollection, new Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartManager());
IEnumerable<KeyValuePair<string, string>> confValues = new List<KeyValuePair<string, string>>()
{
new KeyValuePair<string, string>("TransactionLogging:Enabled", "True"),
new KeyValuePair<string, string>("TransactionLogging:Uri", "https://api.something.com/"),
new KeyValuePair<string, string>("TransactionLogging:Version", "1"),
new KeyValuePair<string, string>("TransactionLogging:Queue:Enabled", "True")
};
ConfigurationBuilder builder = new ConfigurationBuilder();
builder.AddInMemoryCollection(confValues);
var confRoot = builder.Build();
StartupExtensions.YourExtensionMethod(mvcBuilder); // Any other params
}

As an alternative approach to #datchung's answer with ASP.net Core 6 (or 7) Minimal start-up, it's possible to leverage WebApplicationFactory<T> to run startup. Note that this requires defining InternalsVisibleTo from API to test project for the Program reference to be accessible.
Sample test, using xUnit:
[Fact]
public void StartupTest()
{
var waf = new WebApplicationFactory<Program>();
var server = waf.Server;
// Optional: check for individual services
var myService = server.Services.GetService<IMyService>();
Assert.NotNull(myService);
}
The .Server call there triggers the test server and ServiceCollection build. That, in turn, triggers validation unless "ValidateOnBuild" option has been turned off.
More about WAF internals in here: https://andrewlock.net/exploring-dotnet-6-part-6-supporting-integration-tests-with-webapplicationfactory-in-dotnet-6/
All of this does require that your Startup code works in test scenario (it shouldn't connect to online services etc.) but that is also useful for integration testing too (e.g. Alba).

In my case, I'm using .NET 6 with the minimal API (no Startup class).
My Program.cs originally looked like this:
// using statements
...
var builder = WebApplication.CreateBuilder(args);
...
builder.services.AddSingleton<IMyInterface, MyImplementation>();
...
I added StartupHelper.cs:
public class StartupHelper
{
private readonly IServiceCollection _services;
public StartupHelper(IServiceCollection services)
{
_services = services;
}
public void SetUpServices()
{
_services.AddSingleton<IMyInterface, MyImplementation>();
}
}
I used StartupHelper in Program.cs:
// using statements
...
var builder = WebApplication.CreateBuilder(args);
...
var startupHelper = new StartupHelper(builder.Services);
startupHelper.SetUpServices();
...
And my test (NUnit) looks like this:
[Test]
public void SetUpServices()
{
var builder = WebApplication.CreateBuilder(new string[0]);
var startupHelper = new StartupHelper(builder.Services);
startupHelper.SetUpServices();
var app = builder.Build();
var myImplementation = app.Services.GetService<IMyInterface>();
Assert.NotNull(myImplementation);
Assert.IsTrue(myImplementation is MyImplementation);
}

You should be install to Xunit project then add startup.cs file in base directory .

Related

Mapster Global Configuration with Dependency Injection

I'd like to know if there is a way to globally configure Mapster while using Dependency Injection?
The configuration options appear to be for the static usage and also for a singleton pattern only.
Mapster Configuration
Mapster Dependency Injection
I have created an extension method.
// Extension method
public static IServiceCollection AddMapster(this IServiceCollection services, Action<TypeAdapterConfig> options = null)
{
var config = new TypeAdapterConfig();
config.Scan(Assembly.GetAssembly(typeof(Startup)));
options?.Invoke(config);
services.AddSingleton(config);
services.AddScoped<IMapper, ServiceMapper>();
return services;
}
// Called in Startup.ConfigureServices(IServiceCollection services)
services.AddMapster(options =>
{
options.Default.IgnoreNonMapped(true); // Does not work.
TypeAdapterConfig.GlobalSettings.Default.IgnoreNonMapped(true); // Does not work.
});
I imagine these don't work because the ServiceMapper is creating its own instance without using anything I've configured.
I implemented Mapster in a Blazor Server application, and I struggled to find documentation on how to scan the assembly for mapping registrations.
I have a class in my application that implements the IRegister interface and defines the mappings
public class MappingRegistration : IRegister
{
void IRegister.Register(TypeAdapterConfig config)
{
config.NewConfig<ModelA, ModelB>();
}
}
In the ConfigureServices of the Startup.cs I have this then
var typeAdapterConfig = TypeAdapterConfig.GlobalSettings;
// scans the assembly and gets the IRegister, adding the registration to the TypeAdapterConfig
typeAdapterConfig.Scan(Assembly.GetExecutingAssembly());
// register the mapper as Singleton service for my application
var mapperConfig = new Mapper(typeAdapterConfig);
services.AddSingleton<IMapper>(mapperConfig);
I hope this can save someone's time. If anybody is aware of better ways, please let me know.
You can change from
var config = new TypeAdapterConfig();
to
var config = TypeAdapterConfig.GlobalSettings;

.NET Core container definition for NUnit test project

I am quite new to .NET Core. How can I define a DI container within the NUnit class library project?
I know that it is done through IServiceCollection, but since there isn't any Startup method, I don't know where to get the instance implementing this interface.
Also I would like to be able to load definitions from other class libraries (being the subject of testing). That should be simpler as I can just create a static method in that class library with one parameter that is IServiceCollection, but again, how do I get it?
A side question is: I presume some of the interfaces could be mocked for the purpose of tests, but how can I replace a mapping already created using of of IServiceCollection's methods like AddSingleton or AddTransient?
There is a Remove method, but it is not documented.
IServiceCollection is implemented by the ServiceCollecion class. So if you want to do this for integration tests then you can use the ServiceCollection class to create your own ServiceProvider.
var services = new ServiceCollection();
services.AddTransient<IMyInterface, MyClass>();
services.AddScoped<IMyScopedInteface, MyScopedClass>();
...
var serviceProvider = sc.BuildServiceProvider();
You can now use the serviceProvider instance in your tests to get your classes:
var myClass = serviceProvider.GetService<IMyInterface>();
If you want to mock some of the interfaces instead of using the real ones then, instead of adding the real class/interface into the service collection you can add a mock instead:
mockInterface = new Mock<IMyInterface>();
sc.AddScoped<IMyInterface>(factory => mockInterface.Object);
Generally you don't want to create a DI container for your tests but, as you realise, you want to mock them instead. So, for example, if this is a class you want to test:
public class UserService
{
private readonly IUserDatabase _userDatabase;
public UserService(IUserDatabase userDatabase)
{
_userDatabase = userDatabase;
}
public bool DoesUserExist(int userId)
{
return _userDatabase.UserExists(userId);
}
}
And this is the definition of the interface used:
public interface IUserDatabase
{
bool UserExists(int userId);
}
In our tests we can mock the interface to return a specific value we want for our test:
[TestClass]
public class UserServiceTests
{
[TestMethod]
public void DoesUserExist_ForValidUserId_ReturnsTrue()
{
var fakeUserId = 123;
var mockUserDatabase = new Mock<IUserDatabase>();
mockUserDatabase.Setup(udb => udb.UserExists(fakeUserId)).Returns(true);
var userService = new UserService(mockUserDatabase.Object);
var result = userService.DoesUserExist(fakeUserId);
Assert.IsTrue(result);
mockUserDatabase.VerifyAll();
}
}
So in this test we have used Moq to create a mock of our interface. We don't need to use a DI container because we are in controller of creating the class we are testing. The DI container is of more use in production as it enables the application to create any dependencies it needs without your code having to call new - which is a big problem if you are trying to unit test your classes.
The .VerifyAll() method checks that any methods set up on the mock object, in this case we setup a call to UserExists, was actually called.
There are plenty of examples of how to use Moq and mocking interfaces in general. A quickstart guide to Moq is here.

Can not resolve a generic service using manual interception with ASP.NET Core 2 and Castle.Core

I know there are a lot of question similar to this one but actually none of them solved my issue.
I created a new Asp.Net Core 2 application.
Now I am trying to use an intercepter for a specific service to fetch some data into this service(I am using Castle.Core nuget package).
I have a generic IConfigurationInterceptor<> and a real implementation ConfigurationInterceptor<>
Here is the interface:
public interface IConfigurationInterceptor<T> : IInterceptor where T : class { }
public class ConfigurationInterceptor<T> : IConfigurationInterceptor<T> where T : class
{
public ConfigurationInterceptor(ConfigurationInfo<T> configurationInfo,
some other services)
{
_configurationInfo = configurationInfo;
//.....
}
public void Intercept(IInvocation invocation)
{
invocation.ReturnValue = somefunc(someconfig, invocation.Arguments);
}
}
Then I have an extension method like below:
public static void AddSingletonConfiguration<TInterface, TImplementation>(
this IServiceCollection services, string featureName)
where TImplementation : class, TInterface where TInterface : class
{
var info = new ConfigurationInfo<TImplementation>(featureName, typeof(TInterface));
var generator = new ProxyGenerator();
services.AddSingleton(x =>
{
var ic = x.GetService<Func<ConfigurationInfo<TImplementation>,
IConfigurationInterceptor<TImplementation>>>();
var icTemp = ic.Invoke(info);
return (TInterface) generator.CreateInterfaceProxyWithoutTarget(
info.ServiceType, icTemp);
});
}
But when I get to this line of code:
var ic = x.GetService<Func<ConfigurationInfo<TImplementation>,
IConfigurationInterceptor<TImplementation>>>();
it returns me a null value for ic:
ConfigurationInfo class is just a simple class I create for storing some extra data.
public sealed class ConfigurationInfo<TImpl>
{
public Type ServiceType { get; }
public string FeatureName { get; }
public ConfigurationInfo(string featureName, Type serviceType)
{
FeatureName = featureName;
ServiceType = serviceType;
}
public override string ToString()
=> $"{FeatureName} ({ServiceType} -> {typeof(TImpl)})";
}
In my ConfigureServices I have these both lines:
services.AddSingleton(typeof(IConfigurationInterceptor<>),
typeof(ConfigurationInterceptor<>));
services.AddSingletonConfiguration<IStaticDataConfiguration, StaticDataConfiguration>(
"SomeFeatureKey");
I am not sure why ic variable is null because previously another project was using Autofac and was working perfectly but in the startup you would find something like this:
builder.RegisterGeneric(typeof(ConfigurationInterceptor<>))
.As(typeof(IConfigurationInterceptor<>)).SingleInstance();
builder.RegisterConfiguration<IStaticDataConfiguration, StaticDataConfiguration>(
"SomeFeatureKey");
and the extension method was like this one:
public static void RegisterConfiguration<TInterface, TImplementation>(
this ContainerBuilder builder, string featureName)
where TImplementation : class, TInterface
{
var info = new ConfigurationInfo<TImplementation>(featureName, typeof(TInterface));
var generator = new ProxyGenerator();
builder
.Register(c =>
{
var ic = c.Resolve<Func<ConfigurationInfo<TImplementation>,
IConfigurationInterceptor<TImplementation>>>()(info);
return generator.CreateInterfaceProxyWithoutTarget(info.ServiceType, ic);
})
.As<TInterface>()
.SingleInstance();
}
Any help would be appreaciated.
EDIT 1:
Now I changed from method GetService<> to method GetRequiredService<> and throws an exception like below:
No service for type 'System.Func'2[StaticDataProvider.DomainModel.ConfigurationInfo'1[StaticDataProvider.Services.StaticDataConfiguration],StaticDataProvider.Services.Interfaces.IConfigurationInterceptor'1[StaticDataProvider.Services.StaticDataConfiguration]]' has been registered.
EDIT 2:
To wrap it up here is the issue: In my current project in Asp.Net core I can not get a Func<X, B> while in the Asp.Net MVC 5 project(It is a whole different project) I can get a Func<X, B> using Autofac. I think this has to do with parametrized instantiation feature in Autofac provided by default: here
Now, I dont know if in Asp.Net Core default DI container has something like this 'parametrized instantiation' feature where it allows me resolving Func<X, B> instead of B.
I'm guessing the root of the problem is in the fairly complex manual wiring up of the interceptors.
If you're using interceptors with Autofac, it'd be better to use the Autofac.Extras.DynamicProxy2 package and wire up interceptors using the built-in Autofac functionality instead of trying to chain a bunch of resolutions together with functions and parameters. I see a lot of little gotchas in here like how you're setting up a singleton interface proxy without a target but I'm not entirely clear how the target gets added post-facto. There's a lot of complexity you can avoid by using the tools provided.
That said, I'm also looking at the exception message. Without a stack trace I can't 100% guarantee it, but a search on the Autofac source indicates that's not a message that came from Autofac - it's likely, then, a message from the default Microsoft.Extensions.DependencyInjection container. That indicates you may not actually have everything wired up the way you think you do.
I'd back up a bit and just get simple things working and ensure they're coming from Autofac. If you decide you don't want Autofac in play, make sure you've removed it entirely from the equation. Basically, just make sure it's clean and working in the general sense.
After that, add things back slowly, one at a time. I might recommend putting a reproduction in a unit test where you use these registration mechanisms and get things working without the complexity of the entire app weighing down. Unwind it from there. If it's too complex to unit test... maybe that's an indicator you should simplify it and refactor. Make it testable.
I'll leave my previous answer for posterity, but... The default Microsoft IoC provider is very simple and does not support all the features of Autofac. You won't get parameterized resolution or auto-generated factories from it.
Here is what I had to do:
Modified ConfigureService method like below:
public void ConfigureServices(IServiceCollection services)
{
IConfigurationInterceptor<T> GetConfigurationInterceptor<T>(ConfigurationInfo<T> info) where T : class
{
return new ConfigurationInterceptor<T>(info, services.GetService<IConfigurationProvider>(), Configuration);
}
services.AddSingletonConfiguration<IStaticDataConfiguration, StaticDataConfiguration>("someFeatureKey", GetConfigurationInterceptor);
}
Then modified extension methods like below:
public static void AddSingletonConfiguration<TInterface, TImplementation>(this IServiceCollection services,
string featureName, Func<ConfigurationInfo<TImplementation>, IConfigurationInterceptor<TImplementation>> ic) where TImplementation : class, TInterface where TInterface : class
{
var info = new ConfigurationInfo<TImplementation>(featureName, typeof(TInterface));
var generator = new ProxyGenerator();
services.AddSingleton(x =>
{
var icTemp = ic.Invoke(info);
return (TInterface) generator.CreateInterfaceProxyWithoutTarget(info.ServiceType, icTemp);
});
}
public static TInterface GetService<TInterface>(this IServiceCollection services) where TInterface : class
{
var serviceProvider = services.BuildServiceProvider();
return serviceProvider.GetRequiredService<TInterface>();
}
Now its working fine but the idea is that I had to create Func<X, B> myself and pass as a parameter to extension method.

Building a Service Layer in .NET Core WebApp. Constructor DI isnt working

i'd like to implement a Service Layer within my .NET Core WebApp but i have some problems with the Constructor Dependency Injection and no clue what i am doing wrong. Maybe someone can show me my mistake(s).
The Idea about the Services is that they hold the Business Logic and are the only ones which speak to Repositories. Due the Fact every Service has its own responsibility, they also have to speak not only with their Repositories but also with other Services depending on which Business Task must be achived.
To do this i register first my Repositories like this:
Startup.cs
public void ConfigureServices(IServiceCollection services) {
this.storageConnectionString = "Server=.;Initial Catalog=MyDatabase;Integrated Security=True;";
services.AddScoped<IDeviceRepository>(r => new EntityDeviceRepository(this.storageConnectionString));
services.AddScoped<IMasterDataRepository>(r => new EntityMasterDataRepository(this.storageConnectionString));
...
(in the same file and function) i register my Services like this:
var serviceProvider = services.BuildServiceProvider();
services.AddScoped<IDeviceService>(r => new DeviceService(
serviceProvider.GetService<IDeviceRepository>(),
serviceProvider.GetService<IMasterDataRepository>(),
));
services.AddScoped<IHardwareDataService>(r => new HardwareDataService(
serviceProvider.GetService<IDeviceService>(),
serviceProvider.GetService<IHardwareDataRepository>()
));
...
}
IHardwareDataService.cs
public interface IHardwareDataService
{
Task<Boolean> UpdateHardwareData(HardwareData hardwareData);
}
HardwareDataService.cs
public class HardwareDataService : BaseService, IHardwareDataService
{
private IDeviceService deviceService;
private IHardwareDataRepository hardwareDataRepository;
public HardwareDataService(IDeviceService deviceService, IHardwareDataRepository hardwareDataRepository)
{
this.deviceService = deviceService;
this.hardwareDataRepository = hardwareDataRepository;
}
public async Task<Boolean> UpdateHardwareData(HardwareData hardwareData)
{
// Create the Device via the Device Service if not already existing (Business Logic)
// Update Hardware Data of Device (Business Logic + direct Access via HWDataRepository)
}
}
The Problem
The "IDeviceService" is working without Problems.
The "IHardwareDataService" not. To be exact the Object "IDeviceService" is null.
It seems like i can't provide a Service to another Service via Constructor Dependency Injection, but i don't understand why and how to make it the right way.
Any tip about how I can solve this Problem?
There is really no need to build the provider in this scenario.
Review how you register the services, which can be simplified to...
services.AddScoped<IDeviceService, DeviceService>();
services.AddScoped<IHardwareDataService, HardwareDataService>();
The framework will inject the necessary dependencies when resolving the implementations.
Reference: Introduction to Dependency Injection in ASP.NET Core: Registering Your Own Services
Try replace
var serviceProvider = services.BuildServiceProvider();
services.AddScoped<IDeviceService>(r => new DeviceService(
serviceProvider.GetService<IDeviceRepository>(),
serviceProvider.GetService<IMasterDataRepository>(),
));
services.AddScoped<IHardwareDataService>(r => new HardwareDataService(
serviceProvider.GetService<IDeviceService>(),
serviceProvider.GetService<IHardwareDataRepository>()
));
to
services.AddScoped<IDeviceService,DeviceService >();
services.AddScoped<IHardwareDataService,HardwareDataService>();

How to link Autofac to UnitTesting

Within my Web API I have linked Autofac as IoC container, and I do it like this:
Domain level
public class Autofac
{
protected ContainerBuilder Builder { get; set; }
public Autofac()
{
this.Builder = new ContainerBuilder();
}
public virtual IContainer Register()
{
// Register dependencies
SetUpRegistration(this.Builder);
// Build registration.
var container = this.Builder.Build();
// End
return container;
}
private static void SetUpRegistration(ContainerBuilder builder)
{
// === DATALAYER === //
// MyRepository
builder.RegisterType<MyRepository>()
.As<IMyRepository>()
.InstancePerLifetimeScope();
// === DOMAIN === //
// MyManager
builder.RegisterType<MyManager>()
.As<IMyManager>()
.InstancePerLifetimeScope();
}
}
Web API
public class Autofac : Domain.IoC.Autofac
{
public IContainer Register(HttpConfiguration config)
{
// Register your Web API controllers.
base.Builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
// OPTIONAL: Register the Autofac filter provider.
base.Builder.RegisterWebApiFilterProvider(GlobalConfiguration.Configuration);
// Complete registration and get container instance.
var container = base.Register();
// Set the dependency resolver to be Autofac.
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
// Done.
return container;
}
}
As you see it inherits from the base class from Domain and sets up Web API specific config.
Usage
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
new IoC.Autofac().Register(GlobalConfiguration.Configuration);
}
Which is at global.asax, as you know.
The question
This works fine for Web API, but I haven't got a clue what I need to do to register all this within a UnitTest project context.
The idea is that I would create a similar implementation to the Autofac class at Web API level, but than with mocks (completely ignoring the base class from Domain).
Any pointers?
Personally I never see the need (and I struggle to comprehend how viable or helpful it would be) to setup my IoC container directly within a unit test.
As a unit test is used to test a logical piece of code that can be quickly built, easily ran and doesn't require much (I'd advocate no) tear-down. It should not require all of your application to be be setup for the test to run.
Remember that your unit test is simply testing the flow of data through the system i.e that your DomainManager is actually going to call a IRepository when you expect that it should. Then you would have separate test classes for all your repositories to determine that they would correctly add to the database etc.
I'm not sure how you use the DBContext class but as an example of a wrapper this is what it would sort of look like.
interface IDBSetWrapper
{
object Add(object entity);
}
interface IDBContextWrapper
{
...
IDBSet Set(Type entityType);
...
}
class DBContextWrapper : IDBContextWrapper
{
private readonly DBContext context;
public DBContextWrapper()
{
context = new DBContext();
}
...
public IDBSet Set(Type entityType)
{
var dbSet = context.Set(entityType);
return new DBSetWrapper(dbSet);
}
...
}
It's not much but I hope that it demonstrates what I mean about a thin wrapper. Basically the wrapper is the DBContext and will contain an instance of it within the class, the actual DBContext will be called when you request the wrapper to do anything.
I have shown what would happen when returning another object (in this case a DBSet), this will also be wrapped in a separate object with an interface. This is so that you can mock the returns from this class easily.
You can add this new wrapper into your IoC a little better now as it provides an interface.
One thing to note is that you won't be able to and probably wouldn't wish to test the wrapper class, there would be very little point as I see it. But previously I've seen colleagues do an integration test on these sort of classes.

Categories

Resources