How can i mock data access layer using Rhino mocks
I have the following classes:
public interface IDataAccess
{
int ExecuteNoneQuery(SqlConnection connection, string storedProcedureName,
IEnumerable<SqlParameter> sqlParameters);
}
public class DataAccess : IDataAccess
{
public int ExecuteNoneQuery(SqlConnection connection, string storedProcedureName,
IEnumerable<SqlParameter> sqlParameters)
{
using (SqlCommand command = connection.CreateCommand())
{
// do some stuff
return command.ExecuteNonQuery();
}
}
}
public class DbOperation<T>
{
private IDataAccess _access;
public DbOperation(IDataAccess access)
{
_access = access;
}
public int Insert(T item, SqlConnection connection,string spName)
{
IDbObjectFactory<T> parametersFactory = new SqlParameterFactory<T>();
var parameters = (IList<SqlParameter>)parametersFactory.GetDbObject(item);
return _access.ExecuteNoneQuery(connection, spName, parameters);
}
}
Here is an example with some explanations:
To test the case:
WHEN method ExecuteNoneQuery of class DataAccess is called
THEN command.ExecuteNonQuery() should be called:
// 1. Create `SqlCommand` Mock:
var command = MockRepository.GenerateMock<SqlCommand>();
// 2. Create `SqlConnection` Stub:
var connection = MockRepository.GenerateStub<SqlConnection>();
// 3. Setup connection.CreateCommand() to return mocked command:
connection
.Stub(c => c.CreateCommand())
.Return(command);
// 4. Do test action:
var dataAccess = new DataAccess();
dataAccess.ExecuteNoneQuery(connection, null, null);
// Assert command.ExecuteNonQuery() has been called:
command.AssertWasCalled(c => c.ExecuteNonQuery());
Hope that explains a bit how to use Rhino Mock.
I assume you want to test DbOperation by mocking DataAccess.
public void TestInsert()
{
var dataAccess = MockRepository.GenerateMock<IDataAccess>();
var dbOperation = new DbOperation<string>(dataAccess);
var sqlConnection = new SqlConnection();
dbOperation.Insert("blah", sqlConnection, "MySP");
dataAccess.AssertWasCalled(a => a.ExecuteNoneQuery(
Arg.Is(sqlConnection),
Arg.Is("MySP"),
Arg<IEnumerable<SqlParameter>>.Is.Anything));
}
Ideally, you may want to even inject IDbObjectFactory<T> so that you can test the interaction between DbOperation and the sqlParameterFactory - and hence you could also check that ExecuteNoneQuery was called with the exact parameters that sqlParameterFactory would return.
Related
I am trying to test a method with the name GetAll in a xUnit-Project of the class AuhtorRepository.
public class AuthorRepository : IAuthorRepository
{
private readonly ISqlDb _db;
public string Connection { get; set; }
public AuthorRepository(ISqlDb db)
{
_db = db;
}
public async Task<List<AuthorModel>> GetAll()
{
string sql = "SELECT * FROM Author";
List<AuthorModel> authors = await _db.LoadDataAsync<AuthorModel, dynamic>(sql, new { }, Connection);
return authors;
}
}
In this method is the LoadDataAsync method used which I will mock in my test.
The structure of the interface looks like this.
public interface ISqlDb
{
Task<List<T>> LoadDataAsync<T, U>(string sql, U parameters, string connection);
}
Finally the implementation of the test my xUnit-Project.
using Autofac.Extras.Moq;
using DataAccess.Library;
using Moq;
using Repository.Library.Models;
using Repository.Library.Repositories;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xunit;
namespace RespositoryTests
{
public partial class AuhtorRepositoryTests
{
[Fact]
public async Task GetAll_ShouldWorkd()
{
using (var autoMock = AutoMock.GetLoose())
{
//Arrange
autoMock.Mock<ISqlDb>()
.Setup(x => x.LoadDataAsync<AuthorModel, dynamic>("SELECT * FROM Author", new { }, " "))
.ReturnsAsync(GetSamples());
//Act
var cls = autoMock.Create<AuthorRepository>();
cls.Connection = " ";
var expected = GetSamples();
var acutal = await cls.GetAll();
//Assert
Assert.Equal(expected.Count, acutal.Count);
}
}
private List<AuthorModel> GetSamples()
{
var authors = new List<AuthorModel>();
authors.Add(new AuthorModel { Id = 1, FirstName = "first name", LastName = "lastname" });
authors.Add(new AuthorModel { Id = 3, FirstName = "Angela", LastName = "Merkel" });
return authors;
}
}
}
The sturcture of the project looks like this:
[1]: https://i.stack.imgur.com/F4TPT.png
The test fails with the following statement:
Message: System.NullReferenceException : Object reference not set to an instance of an object.
So I expected the test should pass.
What I tried out so far:
-> I wrote a similar project with the same code, the only difference there is the project structure. Every interface and class was in the same Test-Project and the test passed.
Whereas in my BookApp Solution the ISqlDb is suited in the DataAccess.Library Project.
So it looks like that in the mocking the ReturnAsync method is not returning the values
from my GetSamples.
Please bear in mind that
a) This is my first post on stackoverflow and
b) I try to make my first steps in mocking ...
tried out:
//Act
//var cls = autoMock.Create<AuthorRepository>();
//cls.Connection = "";
var cls = new AuthorRepository(autoMock.Create<ISqlDb>());
cls.Connection = "";
var expected = GetSamples();
var acutal = await cls.GetAll();
//Assert
Assert.Equal(expected.Count, acutal.Count);
The mock is returning null by default because the invocation of the mocked member does not match what was setup. Reference Moq Quickstart to get a better under standing of how to use MOQ.
You need to use argument matchers here because the anonymous new { } used in setup wont match the reference of the one actually used when exercising the test
//...
autoMock.Mock<ISqlDb>()
.Setup(x => x.LoadDataAsync<AuthorModel, dynamic>("SELECT * FROM Author", It.IsAny<object>(), It.IsAny<string>()))
.ReturnsAsync(GetSamples());
//...
Note the use of It.IsAny<object>() to allow whatever you pass in to be allowed.
With the above change, the test passed as expected.
I am calling code that I cannot change that takes in a SqlConnection and a Transaction created from that SqlConnection. I am attempting to mock this SqlConnection using Moq 4.16.1. I am getting the following error when attempting to unit test the code:
Unable to cast object of type 'Castle.Proxies.IDbConnectionProxy' to type 'System.Data.SqlClient.SqlConnection'.
I have wrapped the SqlConnection in my own wrapper to try to get this to work, but this has not helped.
SqlConnectionWrapper:
using System.Data;
using System.Data.SqlClient;
namespace Wrappers
{
public class SQLConnectionWrapper : ISQLConnectionWrapper
{
public string ConnectionString { get; private set; }
public SQLConnectionWrapper(string connectionString)
{
ConnectionString = connectionString;
}
public IDbConnection Open()
{
return new SqlConnection(ConnectionString);
}
}
}
Unit test:
private Mock<ISQLConnectionWrapper> _mockSqlConnectionWrapper;
[SetUp]
public void Setup()
{
_mockSqlConnectionWrapper = new Mock<ISQLConnectionWrapper>();
var mockSQLConnection = new Mock<IDbConnection>();
mockSQLConnection.SetupAllProperties();
_mockSqlConnectionWrapper.Setup(x => x.Open())
.Returns(mockSQLConnection.Object);
}
Any help with this would be greatly appreciated.
For example, I have a Repository class for getting data from a database, and there are several service classes, let's say Service1, Service2, Service3.
I will have multiple Repository instances, for example, for two or three databases. And, you should be able to configure services to work with a specific database.
I can't figure out how to implement these dependencies using the Dependency Injection container.
As far as I understand, I can register the Repository service either as a Singleton, or a new instance will be created for each dependency.
But, I only need two repositories, Repository ("DB1") and Repository ("DB2"), and when creating a service instance, I should be able to choose which database to work with. That is, as an option-Service1(Repository ("DB1")), Service2 (Repository ("DB1")), Service1 (Repository ("DB2")).
For example:
public class Program
{
static void Main()
{
var connectionStringDb1 = "DB1 connection string";
var connectionStringDb2 = "DB2 connection string";
var repositoryDb1 = new Repository(connectionStringDb1);
var repositoryDb2 = new Repository(connectionStringDb2);
var smsSendService1 = new SmsSendService(repositoryDb1);
var smsSendService2 = new SmsSendService(repositoryDb2);
var emailSendService1 = new EmailSendService(repositoryDb1);
smsSendService1.Run();
var tasks = new Task[]
{
smsSendService1.Run(),
smsSendService2.Run(),
emailSendService1.Run()
};
Task.WaitAll(tasks);
}
}
public class Repository
{
private string _connectionString;
public Repository(string connectionString)
{
_connectionString = connectionString;
}
public object GetData()
{
// Getting data from the Database
var data = ...;
return data;
}
}
public class SmsSendService
{
private readonly Repository _repository;
public SmsSendService(Repository repository)
{
_repository = repository;
}
public Task Run()
{
return Task.Run(() =>
{
// Sending SMS in a loop
while (true)
{
var data = _repository.GetData();
// ...
Task.Delay(1000);
}
});
}
}
public class EmailSendService
{
private readonly Repository _repository;
public EmailSendService(Repository repository)
{
_repository = repository;
}
public Task Run()
{
return Task.Run(() =>
{
// Sending Email in a loop
while (true)
{
var data = _repository.GetData();
// ...
Task.Delay(1000);
}
});
}
}
Try to take a look at autofac named instances
I developed and API that uses a helper class to get the database context for each endpoint function. Now I'm trying to write unit tests for each endpoint and I want to use an In-memory db in my unit test project.
The issue I'm running into is that in order to call the API functions I had to add a constructor to my API controller class. This would allow me to pass the dbContext of the in-memory db to the controller function for it to use. However, since the adding of the constuctor I got the following error when attempting to hit the endpoint:
"exceptionMessage": "Unable to resolve service for type 'AppointmentAPI.Appt_Models.ApptSystemContext' while attempting to activate 'AppointmentAPI.Controllers.apptController'."
UPDATE
controller.cs
public class apptController : Controller
{
private readonly ApptSystemContext _context;
public apptController(ApptSystemContext dbContext)
{
_context = dbContext;
}
#region assingAppt
/*
* assignAppt()
*
* Assigns newly created appointment to slot
* based on slotId
*
*/
[Authorize]
[HttpPost]
[Route("/appt/assignAppt")]
public string assignAppt([FromBody] dynamic apptData)
{
int id = apptData.SlotId;
string json = apptData.ApptJson;
DateTime timeStamp = DateTime.Now;
using (_context)
{
var slot = _context.AppointmentSlots.Single(s => s.SlotId == id);
// make sure there isn't already an appointment booked in appt slot
if (slot.Timestamp == null)
{
slot.ApptJson = json;
slot.Timestamp = timeStamp;
_context.SaveChanges();
return "Task Executed\n";
}
else
{
return "There is already an appointment booked for this slot.\n" +
"If this slot needs changing try updating it instead of assigning it.";
}
}
}
}
UnitTest.cs
using System;
using Xunit;
using AppointmentAPI.Controllers;
using AppointmentAPI.Appt_Models;
using Microsoft.EntityFrameworkCore;
namespace XUnitTest
{
public abstract class UnitTest1
{
protected UnitTest1(DbContextOptions<ApptSystemContext> contextOptions)
{
ContextOptions = contextOptions;
SeedInMemoryDB();
}
protected DbContextOptions<ApptSystemContext> ContextOptions { get; }
private void SeedInMemoryDB()
{
using(var context = new ApptSystemContext(ContextOptions))
{
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
var seventh = new AppointmentSlots
{
SlotId = 7,
Date = Convert.ToDateTime("2020-05-19 00:00:00.000"),
Time = TimeSpan.Parse("08:45:00.0000000"),
ApptJson = null,
Timestamp = null
};
context.AppointmentSlots.Add(seventh);
context.SaveChanges();
}
}
[Fact]
public void Test1()
{
DbContextOptions<ApptSystemContext> options;
var builder = new DbContextOptionsBuilder<ApptSystemContext>();
builder.UseInMemoryDatabase();
options = builder.Options;
var context = new ApptSystemContext(options);
var controller = new apptController(context);
// Arrange
var request = new AppointmentAPI.Appt_Models.AppointmentSlots
{
SlotId = 7,
ApptJson = "{'fname':'Emily','lname':'Carlton','age':62,'caseWorker':'Brenda', 'appStatus':'unfinished'}",
Timestamp = Convert.ToDateTime("2020-06-25 09:34:00.000")
};
string expectedResult = "Task Executed\n";
// Act
var response = controller.assignAppt(request);
Assert.Equal(response, expectedResult);
}
}
}
InMemoryClass.cs
using System;
using System.Data.Common;
using Microsoft.EntityFrameworkCore;
using AppointmentAPI.Appt_Models;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore.Infrastructure;
namespace XUnitTest
{
public class InMemoryClass1 : UnitTest1, IDisposable
{
private readonly DbConnection _connection;
public InMemoryClass1()
:base(
new DbContextOptionsBuilder<ApptSystemContext>()
.UseSqlite(CreateInMemoryDB())
.Options
)
{
_connection = RelationalOptionsExtension.Extract(ContextOptions).Connection;
}
private static DbConnection CreateInMemoryDB()
{
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
return connection;
}
public void Dispose() => _connection.Dispose();
}
}
The exception suggests that you haven't registered your DBContext in your Startup.cs (as mentioned above). I'd also suggest that you change the name of your private readonly property to something other than DbContext (which is the class name and can get confusing)
Use something like this:
private readonly ApptSystemContext _context;
Besides that, your approach should be changed.
First, you will set the connection string when you register the DBContext. Just let dependency injection take care of that for you. Your controller should look like this:
public apptController(ApptSystemContext dbContext)
{
_context = dbContext;
}
The dbContext won't be null if you register it in Startup.
Next, unit testing is a tricky concept, but once you write your Unit test, you'll start to understand a little better.
You've said that you want to use the SQL In Memory db for unit testing, which is a good approach (be aware that there are limitations to SQL In Mem like no FK constraints). Next, I assume you want to test your Controller, so, since you MUST pass in a DBContext in order to instantiate your Controller, you can create a new DBContext instance that is configured to use the In Memory Database.
For example
public void ApptControllerTest()
{
//create new dbcontext
DbContextOptions<ApptSystemContext> options;
var builder = new DbContextOptionsBuilder<ApptSystemContext>();
builder.UseInMemoryDatabase();
options = builder.Options;
var context = new ApptSystemContext(options);
//instantiate your controller
var controller = new appController(context);
//call your method that you want to test
var retVal = controller.assignAppt(args go here);
}
Change the body of the method to this:
public string assignAppt([FromBody] dynamic apptData)
{
int id = apptData.SlotId;
string json = apptData.ApptJson;
DateTime timeStamp = DateTime.Now;
using (_context)
{
var slot = _context.AppointmentSlots.Single(s => s.SlotId == id);
// make sure there isn't already an appointment booked in appt slot
if (slot.Timestamp == null)
{
slot.ApptJson = json;
slot.Timestamp = timeStamp;
_context.SaveChanges();
return "Task Executed\n";
}
else
{
return "There is already an appointment booked for this slot.\n" +
"If this slot needs changing try updating it instead of assigning it.";
}
}
}
Another suggestion, don't use a dynamic object as the body of a request unless you are absolutely forced to do so. Using a dynamic object allows for anything to be passed in and you lose the ability to determine if a request is acceptible or not.
I'm implementing an ASP.NET MVC application and need to implement the Unit Of Work with repositories pattern. My implementation is designed as follows:
The UnitOfWork object is in charge of issuing COMMITs and ROLLBACKs as necessary.
The UnitOfWork object contains a Transaction property, obtaied from the internal DB connection. This object provides atomicity to the operations inside the UnitOfWork.
The UnitOfWork object contains the repositories as properties injected at runtime.
Each repository needs to be provided the IDbTransaction object that UnitOfWork created to support atomicity.
So now I find myself in the strange situation of, in UnitOfWork, having to inject repositories that need a property of UnitOfWork itself in order to be instantiated. So my question is: How do I do this? Or maybe something in the design has to be changed?
I'm using SQL Server at the moment and I use Dapper for the SQL calls. Also, I'm thinking of using Autofac as DI framework.
What I've done so far is to implement UOW and a sample repository. Code as follows.
IRepository.cs:
public interface IRepository<TObj, TKey>
{
Task<TObj> DetallesAsync(TKey id);
Task<TKey> AgregarAsync(TObj obj);
}
DbRepository.cs:
public abstract class DbRepository
{
private readonly IDbConnection _connection;
private readonly IDbTransaction _transaction;
protected IDbConnection Connection
{
get => _connection;
}
protected IDbTransaction Transaction
{
get => _transaction;
}
public DbRepository(IDbTransaction transaction)
{
_transaction = transaction;
_connection = _transaction.Connection;
}
}
RolRepository.cs:
public class MSSQLRolRepository : DbRepository, IRolRepository
{
public MSSQLRolRepository(IDbTransaction transaction)
: base(transaction)
{
}
public async Task<int> AgregarAsync(Rol obj)
{
var result = await Connection.ExecuteScalarAsync<int>(MSSQLQueries.RolAgregar, param: obj, transaction: Transaction);
return result;
}
public async Task<Rol> DetallesAsync(int id)
{
var param = new { Id = id };
var result = await Connection.QuerySingleOrDefaultAsync<Rol>(MSSQLQueries.RolDetalles, param: param, transaction: Transaction);
return result;
}
public async Task<Rol> DetallesPorNombreAsync(string nombre)
{
var param = new { Nombre = nombre };
var result = await Connection.QuerySingleOrDefaultAsync<Rol>(MSSQLQueries.RolDetallesPorNombre, param: param, transaction: Transaction);
return result;
}
public async Task<Rol[]> ListarAsync(int pagina, int itemsPorPagina)
{
var param = new { Pagina = pagina, ItemsPorPagina = itemsPorPagina };
var result = await Connection.QueryAsync<Rol>(MSSQLQueries.RolListar, param: param, transaction: Transaction);
return result.ToArray();
}
public async Task<Rol[]> ListarTodosAsync()
{
var result = await Connection.QueryAsync<Rol>(MSSQLQueries.RolListar, transaction: Transaction);
return result.ToArray();
}
}
IUnitOfWork.cs:
public interface IUnitOfWork : IDisposable
{
IDbTransaction Transaction { get; }
IDenunciaRepository DenunciaRepository { get; }
IUsuarioRepository UsuarioRepository { get; }
IRolRepository RolRepository { get; }
void Commit();
void Rollback();
}
MSSQLUnitOfWork.cs:
public class MSSQLUnitOfWork : IUnitOfWork
{
private bool _already_disposed = false;
private IDbConnection _connection;
private IDbTransaction _transaction;
private IDenunciaRepository _denuncia_repository;
private IUsuarioRepository _usuario_repository;
private IRolRepository _rol_repository;
public IDbTransaction Transaction
{
get => _transaction;
}
public IDenunciaRepository DenunciaRepository
{
get => _denuncia_repository;
}
public IUsuarioRepository UsuarioRepository
{
get => _usuario_repository;
}
public IRolRepository RolRepository
{
get => _rol_repository;
}
public MSSQLUnitOfWork()
{
var connection_string = ConfigurationManager.ConnectionStrings["MSSQL"].ConnectionString;
_connection = new SqlConnection(connection_string);
_connection.Open();
_transaction = _connection.BeginTransaction();
//TODO: Crear repos con transacción
}
public void Commit()
{
_transaction.Commit();
}
public void Rollback()
{
_transaction.Rollback();
}
protected virtual void Dispose(bool disposeManagedObjects)
{
if (!_already_disposed)
{
if (disposeManagedObjects)
{
_transaction?.Dispose();
_connection?.Dispose();
}
_already_disposed = true;
}
}
public void Dispose()
{
Dispose(true);
}
}
I recommend you 3 different things.
Start, Commit and Rollback your data transactions within the repository where you are instantiatign the UnitOfWork - the least I recommend
Create a Service class where you can create an instance of UnitOfWork and pass it the instance or DBContext to the Repositories that you involve in the transactions
Create Repository instance within the UnitOfWork class tha knows the current DBContext then you can access from UnitOfWork the repository operations and starts and ends the transactions in the same context. More recommended
Something like:
UnitOfWorkInstance.MyRepositoryA.AddAsync(...);
UnitOfWorkInstance.MyRepositoryB.AddAsync(...);
UnitOfWorkInstance.Commit();
I find myself in the strange situation of, in UnitOfWork, having to inject repositories that need a property of UnitOfWork itself in order to be instantiated.
It looks like you have a circular dependency and circular dependency is always a bad design and you should break it. If you followed Single Responsibility Principle it should not happens. If it happens the service may have too many responsibilities and you should break it in multiple services or sometime it is because you have too small service and these services should be reunited.
In your case it looks like IUnitOfWork has too many responsibility. What's the goal of this service? what are its responsibilities?
For me, this service should not have any repositories, this service doesn't need any. If any other service needs such a repository, they just have to add a dependency on it. Also the repository don't need to have a dependency on IUnitOfWork but only on IDbTransaction. Furthermore IDbTransaction and IDbConnection should be configured in your dependency injector. If these service need to be instanciated by IUnitOfWork for any reason you can do something like
builder.RegisterType<MSSQLUnitOfWork>()
.As<IUnitOfWork>()
.InstancePerLifetimeScope()
builder.Register(c => c.Resolve<IUnitOfWork>().Transation)
.As<IDbTransaction>();
builder.Register(c => c.Resolve<IUnitOfWork>().Connection)
.As<IDbConnection>();