Mock DBContext class which uses a stored procedure and SqlAdapter to fetch details using Nunit and Moq - entity-framework

I need to Mock a DBContext class which in turn uses a Stored procedure & SqlAdapter to fetch result using Nunit and Moq . There is Unity Resolver IOC involved which takes care of DI .
---Web API Controller Class----
IEmployee _employee;
public Controller(IEmployee employee)
{
_employee = employee;
}
Route[employeedetails/get]
[HttpPost]
public EmployeeEntity Get(ParameterObject param)
{
return _employee.Get(param);
}
----------------------------------
Employee implements IEmployee :
--- DataAccess Class which implements the interface----
public class Employee : IEmployee
{
public EmployeeContext _repository;
public Employee(EmployeeContext repository)
{
_repository = repository;
}
public EmployeeEntity Get(ParameterObject param)
{
DataSet dataset= new DataSet();
EmployeeEntity response = new EmployeeEntity();
using(SqlConnection connection =
(SqlConnection)_repository.Database.Connection)
{
connection.Open()
SqlCommand command = connection.CreateCommand();
command.CommandText = "spGetEmployee";
command.CommandType = CommandType.StoredProcedure;
-- Add Parameters using param object --
using(SqlDataAdapter adapter = new SqlDataAdapter(command))
{
adapter.Fill(dataset);
response = MapReponse(dataset);
}
connection.Close();
}
return response;
}
}
-------------------------------------
--------DBContext class -----------
public partial class EmployeeContext : DbContext
{
public EmployeeContext() : base ("name=EmployeeContext")
{
}
}
So I need to Mock the DB functionality using NUnit and Moq .. My code using Unity resolver . Please help I tried many ways . Nothing si working for me ....

Related

Custom DynamoDb TableNameResolver not being called when using CrudRepository

I am testing DynamoDB tables and want to set up different table names for prod and dev environment using the keyword"dev" for development and prod for production.
I have a POJO
#DynamoDBTable(tableName = "abc_xy_dev_MyProjectName_Employee")
public class Employee implements Cloneable {
}
On Prod I want its name to be abc_xy_prod_MyProjectName_Employee.
So, I wrote a TableNameResolver
public static class MyTableNameResolver implements TableNameResolver {
public static final MyTableNameResolver INSTANCE = new MyTableNameResolver();
#Override
public String getTableName(Class<?> clazz, DynamoDBMapperConfig config) {
final TableNameOverride override = config.getTableNameOverride();
String tableNameToReturn = null;
if (override != null) {
final String tableName = override.getTableName();
if (tableName != null) {
System.out.println("MyTableNameResolver ==================================");
return tableName;
}
}
String env = System.getenv("DEPLOYMENT_ENV");
for(Annotation annotation : clazz.getAnnotations()){
if(annotation instanceof DynamoDBTable){
DynamoDBTable myAnnotation = (DynamoDBTable) annotation;
if ("production".equals(env)){
tableNameToReturn = myAnnotation.tableName().replace("_dev_", "_prod_");
}
else {
tableNameToReturn = myAnnotation.tableName();
}
}
}
return tableNameToReturn;
}
}
This works by creating a table with the name abc_xy_prod_MyProjectName_Employee in production.
However, I have a repository with the following code
#EnableScan
public interface EmployeeRepository extends CrudRepository<Employee, String>
{
#Override
<S extends Employee> S save(S employee);
Optional<Employee> findById(String id);
#Override
List<Employee> findAll();
Optional<Employee> findByEmployeeNumber(String EmployeeNumber);
}
Thus when i try to call the method findAll via a endpoint /test case, i get the exception
There was an unexpected error (type=Internal Server Error,
status=500). User:
arn:aws:iam::87668976786:user/svc_nac_ps_MyProjectName_prod is not
authorized to perform: dynamodb:Scan on resource:
:table/abc_xy_dev_MyProjectName_Employee (Service: AmazonDynamoDBv2;
Status Code: 400; Error Code: AccessDeniedException; Request ID:
aksdnhLDFL)
i.e MyTableNameResolver doesn't get called internally when the respository methods are executed. It still points to table name with the name abc_xy_dev_MyProjectName_Employee given in the annotation #DynamoDBTable(tableName = "abc_xy_dev_MyProjectName_Employee")
You have used spring JPA as persistence dynamoDB Integration.
Below configuration can be used to set table name override as part of spring boot configuration.
Sample example is found in https://github.com/ganesara/SpringExamples/tree/master/spring-dynamo
Map Dynamo db repository with user defined mapper config reference
#EnableDynamoDBRepositories(basePackages = "home.poc.spring", dynamoDBMapperConfigRef="dynamoDBMapperConfig")
Mapper Config for table override is as below
#Bean
public DynamoDBMapperConfig dynamoDBMapperConfig() {
DynamoDBMapperConfig mapperConfig = new DynamoDBMapperConfig
.Builder()
.withTableNameOverride(DynamoDBMapperConfig.TableNameOverride.withTableNamePrefix("PROD_"))
.build();
return mapperConfig;
}
Full configuration for reference
#Configuration
#EnableDynamoDBRepositories(basePackages = "home.poc.spring", dynamoDBMapperConfigRef="dynamoDBMapperConfig")
public class DynamoDBConfig {
#Value("${amazon.dynamodb.endpoint}")
private String amazonDynamoDBEndpoint;
#Value("${amazon.aws.accesskey}")
private String amazonAWSAccessKey;
#Value("${amazon.aws.secretkey}")
private String amazonAWSSecretKey;
#Bean
public AmazonDynamoDB amazonDynamoDB() {
AmazonDynamoDB amazonDynamoDB
= new AmazonDynamoDBClient(amazonAWSCredentials());
if (!StringUtils.isEmpty(amazonDynamoDBEndpoint)) {
amazonDynamoDB.setEndpoint(amazonDynamoDBEndpoint);
}
return amazonDynamoDB;
}
#Bean
public AWSCredentials amazonAWSCredentials() {
return new BasicAWSCredentials(
amazonAWSAccessKey, amazonAWSSecretKey);
}
#Bean
public DynamoDBMapperConfig dynamoDBMapperConfig() {
DynamoDBMapperConfig mapperConfig = new DynamoDBMapperConfig
.Builder()
.withTableNameOverride(DynamoDBMapperConfig.TableNameOverride.withTableNamePrefix("PROD_"))
.build();
return mapperConfig;
}
#Bean
public DynamoDBMapper dynamoDBMapper() {
return new DynamoDBMapper(amazonDynamoDB(), dynamoDBMapperConfig());
}
}
You are using DynamoDBMapper (the Java SDK). Here is how I use it. Lets say I have a table called Users, with an associated User POJO. In DynamoDB I have DEV_Users and LIVE_Users.
I have an environment variable 'ApplicationEnvironmentName' which is either DEV or LIVE.
I create a custom DynamoDBMapper like this:
public class ApplicationDynamoMapper {
private static Map<String, DynamoDBMapper> mappers = new HashMap<>();
private static AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard()
.withRegion(System.getProperty("DynamoDbRegion")).build();
protected ApplicationDynamoMapper() {
// Exists only to defeat instantiation.
}
public static DynamoDBMapper getInstance(final String tableName) {
final ApplicationLogContext LOG = new ApplicationLogContext();
DynamoDBMapper mapper = mappers.get(tableName);
if (mapper == null) {
final String tableNameOverride = System.getProperty("ApplicationEnvironmentName") + "_" + tableName;
LOG.debug("Creating DynamoDBMapper with overridden tablename {}.", tableNameOverride);
final DynamoDBMapperConfig mapperConfig = new DynamoDBMapperConfig.Builder().withTableNameOverride(TableNameOverride.withTableNameReplacement(tableNameOverride)).build();
mapper = new DynamoDBMapper(client, mapperConfig);
mappers.put(tableName, mapper);
}
return mapper;
}
}
My Users POJO looks like this:
#DynamoDBTable(tableName = "Users")
public class User {
...
}
When I want to use the database I create an application mapper like this:
DynamoDBMapper userMapper = ApplicationDynamoMapper.getInstance(User.DB_TABLE_NAME);
If I wanted to a load a User, I would do it like this:
User user = userMapper.load(User.class, userId);
Hope that helps.

one transaction for multiple contexts in integration tests on TestInitialize

I am writing integration tests and I want to use transaction scope.
We use EF and Repositories with Contexts.
If I have one Repository and once Context then it would look like this:
[TestInitialize]
public void RuleEngineTestsStart() {
customContext = new CustomContext();
transaction = customContext.Database.BeginTransaction();
repo = new CustomRepository(customContext);
// I need to make this context to work in the same transaction as above
anotherContext = new AnotherContext();
anotherRepo = new AnotherRepository(anotherContext);
}
At the end of tests (TestCleanup) I would like to transaction.Rollback(); everything.
I want to have the same transaction for all repositories that work with different contexts, is it possible? How to create transaction and 'send' it to all three contexts?
Please, to do not to use one Context for all repositories, it is not possible due to reasons (we want to have each context with its own DbSets later to be used within microservices).
Edit
In comments I was asked to include more code, however, I think is not necessary to answer my question.
customContext = new CustomContext();
repo = new CustomRepository(customContext);
customContext2 = new CustomContext2();
otherRepository = new CustomRepository2(customContext2);
// class to be tested needs both repositories
ToBeTestedClass cl = new ToBeTestedClass(customRepository, otherRepository);
// "BASE" interface
public interface IRepository<TEntity> where TEntity : class
{
TEntity GetById(long id);
IEnumerable<TEntity> GetByFilter(Expression<Func<TEntity, bool>> predicate);
TEntity GetSingleByFilter(Expression<Func<TEntity, bool>> filter);
void Insert(TEntity entity);
void Delete(long id);
void Update(TEntity entity);
...
}
// BASE CLASS
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected readonly DbContext _context;
protected readonly DbSet<TEntity> _dbSet;
public Repository(ColldeskDbContext context)
{
_context = context;
_dbSet = context.Set<TEntity>();
}
// GetSingle, GetAll, Insert, Update etc.
}
// CustomRepository (other Repositories are similar, with custom methods)
public interface ICustomRepository : IRepository<CusotmData>
{
// some specific methods that are not in Base class
}
public class CustomRepository: Repository<CustomData>, ICustomRepository
{
public CustomRepository(CustomContext context) : base(context)
{
}
// custom methods that are specific for given context
}
// Contexts - each context consists of its one DbSets
Don't use dbContext.SaveChanges() in your repositories. Use ONE dbContext when creating repositories. Sample:
using ( var db = new YourDbContext() )
{
// Create and begin transaction
using ( var transaction = db.Database.BeginTransaction() )
{
try
{
// ONE dbContext for all repositories
var firstRepo = new Custom1Repository(db);
var secondRepo = new Custom2Repository(db);
City city = new City { Description = "My city" };
Street street = new Street { Description = "My street", City = city};
firstRepo.Insert(city);
secondRepo.Insert(street);
// Save all your changes and after that commit transaction
db.SaveChanges();
transaction.Commit();
}
catch ( Exception ec)
{
transaction.Rollback();
}
}
}
Doing like this your repositories becomes just wrappers over DbSet<TEntity>
I have figured out that I can simply use TransactionScope like this:
private TransactionScope _scope;
[TestInitialize]
public void TestInitialize()
{
_scope = new TransactionScope();
}
[TestCleanup]
public void TestCleanup()
{
_scope.Dispose();
}
And then each Context would be running within this TransactionScope.

MsTest - Mocking a DbContext with DbContextOption and configuration

I have 2 project, Data and Data.test, I use ef core and .net core for both of them, for Data project I have ExpenseDb like this:
public class ExpenseDb: DbContext
{
private IConfigurationRoot _config;
public ExpenseDb(DbContextOptions<ExpenseDb> options, IConfigurationRoot config) : base(options)
{
_config = config;
}
public DbSet<Account> Accounts { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder.UseSqlServer(_config["Data:ConnectionString"]);
}
}
And I have a repository for Account like this:
private ExpenseDb _db;
public AccountRepository(ExpenseDb db)
{
_db = db;
}
public IEnumerable<Account> All(Guid userId)
{
return (_db.Accounts.AsNoTracking().Where(a => a.UserId == userId).ToList());
}
I use ms IOC for injectiong dependencies like this :
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json");
_config = builder.Build();
}
IConfigurationRoot _config;
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(_config);
services.AddDbContext<ExpenseDb>(ServiceLifetime.Scoped);
}
These all are in my Data project, and for Data.Test I would like to test All method, I realized I must Mock my ExpenseDb so I got Moq from Nuget Package and now I have test class like this :
[TestClass]
public class AccountRepositoryTest
{
private readonly Mock<ExpenseDb> _dbMock = new Mock<ExpenseDb>();
private readonly AccountRepository _repo;
public AccountRepositoryTest()
{
_repo = new AccountRepository(_dbMock.Object);
}
[TestMethod]
public void AllForInvalidUser()
{
var fakeaccount = new Account() { Name="cat2",OpenDate=DateTime.Now,StartBalance=100};
Mock < DbSet < Account >> acMock = DbSetMock.Create(fakeaccount);
var results = _repo.All(Guid.Parse("cf15c6c9-f688-47ee-892e-297e530be053"));
Assert.IsNotNull(results);
}
}
Obviously my test is failed, because I must pass config and options to my ExpenseDb somehow, but I don't know How?!
I searched and I found out all answer are saying "You must have an inteface for your service" but i don't want to create an unnecessary interface.
Since DbContextOptions and config are not being used in the actual test code. You could create a constructor in your db context marked as protected to allow the instantiation of the ExpenseDb object without any params.

Entity Framework Core 1.1 In Memory Database fails adding new entities

I am using the following code in a unit test for the test setup:
var simpleEntity = new SimpleEntity();
var complexEntity = new ComplexEntity
{
JoinEntity1List = new List<JoinEntity1>
{
new JoinEntity1
{
JoinEntity2List = new List<JoinEntity2>
{
new JoinEntity2
{
SimpleEntity = simpleEntity
}
}
}
}
};
var anotherEntity = new AnotherEntity
{
ComplexEntity = complexEntity1
};
using (var context = databaseFixture.GetContext())
{
context.Add(anotherEntity);
await context.SaveChangesAsync();
}
When SaveChangesAsync is reached EF throws an ArgumentException with the following message:
An item with the same key has already been added. Key: 1
I'm using a fixture as well for the unit test class which populates the database with objects of the same types, though for this test I want this particular setup so I want to add these new entities to the in memory database. I've tried adding the entities on the DbSet (not the DbContext) and adding all three entities separatly to no avail. I can however add "simpleEntity" separately (because it is not added in the fixture) but EF complains as soon as I try to add "complexEntity" or "anotherEntity".
It seems like EF in memory database cannot handle several Add's over different instances of the context. Is there any workaround for this or am I doing something wrong in my setup?
The databaseFixture in this case is an instance of this class:
namespace Test.Shared.Fixture
{
using Data.Access;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
public class InMemoryDatabaseFixture : IDatabaseFixture
{
private readonly DbContextOptions<MyContext> contextOptions;
public InMemoryDatabaseFixture()
{
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
var builder = new DbContextOptionsBuilder<MyContext>();
builder.UseInMemoryDatabase()
.UseInternalServiceProvider(serviceProvider);
contextOptions = builder.Options;
}
public MyContext GetContext()
{
return new MyContext(contextOptions);
}
}
}
You can solve this problem by using Collection Fixtures so you can share this fixture across several test classes. This way you don't build you context several times and thus you won't get this exception:
Some information about collection Fixture
My own example:
[CollectionDefinition("Database collection")]
public class DatabaseCollection : ICollectionFixture<DatabaseFixture>
{ }
[Collection("Database collection")]
public class GetCitiesCmdHandlerTests : IClassFixture<MapperFixture>
{
private readonly TecCoreDbContext _context;
private readonly IMapper _mapper;
public GetCitiesCmdHandlerTests(DatabaseFixture dbFixture, MapperFixture mapFixture)
{
_context = dbFixture.Context;
_mapper = mapFixture.Mapper;
}
[Theory]
[MemberData(nameof(HandleTestData))]
public async void Handle_ShouldReturnCountries_AccordingToRequest(
GetCitiesCommand command,
int expectedCount)
{
(...)
}
public static readonly IEnumerable<object[]> HandleTestData
= new List<object[]>
{
(...)
};
}
}
Good luck,
Seb

dapper with autofac and repository pattern

I am using dapper with the repository pattern in a WebApi Application and I have the following problem.
The Repository Class is as follows
public class Repository : DataConnection, IRepository
{
public Repository(IDbConnection connection)
: base(connection)
{
}
public T GetFirst<T>(object filters) where T : new()
{
//Creates the sql generator
var sqlGenerator = new MicroOrm.Pocos.SqlGenerator.SqlGenerator<T>();
//Creates the query
var query = sqlGenerator.GetSelect(filters);
//Execute the query
return Connection.Query<T>(query, filters).FirstOrDefault();
}
The IRepository Interface has only one method, the GetFirst. A Controller that uses this repository is as follows
public class UsersController : ApiController
{
private IRepository Repository;
public UsersController(IRepository repository)
{
Repository = repository;
}
public User Get(int id)
{
return Repository.GetFirst<User>(new { id });
}
}
I use autofac as DI and in the Application_Start method in Global.asax I use the following code
string connString = ConfigurationManager.ConnectionStrings["DapperDemo"].ConnectionString;
SqlConnection connnection = new SqlConnection(connString);
var builder = new ContainerBuilder();
builder.RegisterType<Repository>().As<IRepository>();
builder.RegisterType<UsersController>().InstancePerRequest();
var container = builder.Build();
var resolver = new AutofacWebApiDependencyResolver(container);
GlobalConfiguration.Configuration.DependencyResolver = resolver;
But it seems that I am missing something cause I get the following error:
An error occurred when trying to create a controller of type 'UsersController'. Make sure that the controller has a parameterless public constructor.
You need to overwrite default controller activator, because it has no knowledge of your DI container.
Add a service class:
public class ServiceActivator : IHttpControllerActivator
{
public ServiceActivator(HttpConfiguration configuration) { }
public IHttpController Create(HttpRequestMessage request
, HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
var controller = ObjectFactory.GetInstance(controllerType) as IHttpController;
return controller;
}
}
Then on Application_Start():
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new ServiceActivator(GlobalConfiguration.Configuration));
I'm using structure map in this example, so just replace it with which ever container you are using.