Mocking the database layer, Oracle refCursor, should I refactor this? - c#

I apologize for the verbose posting. I don't like seeing those myself, but my question is about structure and I think all the pieces are needed for asking it.
The interesting part is at the bottom, though, so feel free to scroll down to the question.
Here is a Controller. I am injecting a context and a command factory. The controller returns a list of objects that are read from an Oracle database.
public class aController : ControllerBase
{
protected readonly IDB db;
public aController(IContext context, ICommandFactory factory)
{
db = IDB.dbFactory(context, factory);
}
[HttpGet]
public ActionResult<s> GetS()
{
return Ok(db.DbGetS());
}
}
What is currently not injected is the persistence class. There will be a manageable number of stored procedures that are mapped to a model, all hand-coded to a spec. This interface has a factory to construct an implementation (so that I can mock it should the need arise), and our data retrieval method.
public interface IDB
{
public static IDB dbFactory(
IContext context,
ICommandFactory factory)
{
return new DB(context, factory);
}
public S DbGetS();
}
This class implements the interface. It has a constructor that passes the injected items to the base constructor and otherwise does the Oracle interaction by calling generic access methods in the base class.
public class DB: dbBase, IDB
{
public DB(
IContext context,
ICommandFactory factory)
: base(context, factory)
{ }
public S DbGetS()
{
S s = new S();
IEnumerable<S> ss = GetData("proc-name");
return ss.SingleOrDefault();
}
}
Then there is a base class to all the model classes that uses generics and does the heavy lifting. This is very much simplified.
public abstract class dbBase
{
private readonly IContext _context;
private readonly ICommandFactory _commandFactory;
protected delegate IEnumerable<T> ParseResult<T>(IDbCommand cmd);
protected dbBase(IContext context, ICommandFactory factory)
{
_context = context;
_commandFactory = factory;
}
protected IEnumerable<T> GetData<T>(string sproc)
{
IEnumerable<T> results = null;
var cmd = this._commandFactory.GetDbCommand(sproc, this._context);
// boilerplate code omitted that sets up the command and executes the query
results = parseResult<T>(cmd); // this method will read from the refCursor
return results;
}
private IEnumerable<T> parseResult<T>(IDbCommand cmd) where T : ModelBase, new()
{
// This cast is the problem:
OracleRefCursor rc = (OracleRefCursor)cmd.Parameters["aCursor"];
using (OracleDataReader reader = rc.GetDataReader())
{
while (reader.Read())
{
// code omitted that reads the data and returns it
And here is the Unit Test that should test the Controller:
public void S_ReturnsObject()
{
// Arrange
var mockFactory = new Mock<ICommandFactory>();
var mockContext = new Mock<IContext>();
var mockCommand = new Mock<IDbCommand>();
var mockCommandParameters = new Mock<IDataParameterCollection>();
mockCommandParameters.SetupGet(p => p[It.IsAny<string>()]).Returns(mockParameter.Object);
// Set up the command and parameters
mockCommand.SetupGet(x => x.Parameters)
.Returns(mockCommandParameters.Object);
mockCommand.Setup(x => x.ExecuteNonQuery()).Verifiable();
// Set up the command factory
mockFactory.Setup(x => x.GetDbCommand(
It.IsAny<string>(),
mockContext.Object))
.Returns(mockCommand.Object)
.Verifiable();
var controller = new aController(mockContext.Object, mockFactory.Object);
// Act
var result = controller.GetS();
// omitted verification
All stored procedures have refCursor output parameters that contain the results. The only way to obtain an OracleDataReader for this is to cast the query output parameter to OracleRefCursor. Mocking the reader is therefore not possible, because even though I can get a mock parameter, the test will fail with a cast exception in the ParseResult method. Unless I am missing something.
I fear that I need to cut the Oracle API interactions out, even though it would be nice to at least enter parseResults() as part of the test.
I could inject IDB and replace DbGetS() with a mock version, but then there will be not much code coverage by my test and I won't be able to mock any database connection issues and the like. Also, there will be about a dozen IDB level interfaces that would all have to be injected.
How should I restructure this to be able write meaningful tests?
(Disclaimer: The code snippets that I pasted here are for illustration purposes and were heavily edited. The results were not tested and will not compile or run.)

I didn't really expect an answer, or at least, I am fine with not receiving one. As it often happens when I ask a question on SO, just laying out the problem in a way that others can understand it makes it clear enough for me to see through.
In this instance, the essence of the question boiled down to there not being a way around using GetDataReader() on the RefCursor. In other words, if you have Oracle Stored Procedures with an output cursor (i.e. not a SELECT result set), you cannot mock the database interaction unless you manage to write your own RefCursor and OracleDataReader. If you think this is wrong, please elaborate. OracleCommand, OracleParameter, and operations on the command (ExecuteNonQuery) can be substituted with System.Data equivalencies that can be mocked.
So what did I do? I reverted the substitution of the Oracle.ManageDataAccess types with System.Data stuff (because the former are less verbose) and injected IDB instead.
This is the resulting unit test:
// Arrange
var mockDbS = new Mock<IDB>();
Model.S expected = new Model.S() { var1 = 1, var2 = 2, var3 = 3 };
mockDbS.Setup(d => d.DbGetS().Returns(expected);
var controller = new aController(mockDbS.Object);
// Act
ActionResult<Model.Summary> actionResult = controller.GetS();
Model.S actual = ((ObjectResult)actionResult.Result).Value as Model.S;
// Assert
mockDbS.Verify();
Assert.Equal(200, ((ObjectResult)actionResult.Result).StatusCode);
Assert.Equal(expected, actual);
This does not give me the coverage that I wanted, but it is a basic test of the controller actions.

Related

C#: Testing Entity Framework FromSql to ensure proper syntax

I am writing to test FromSql Statement with InMemory Database. We are attempting to utilize Sqlite.
Running the following Sql passes the unit test without error.
select * from dbo.Product
However, doing this also passes with incorrect sql syntax. Would like to make the test fail with improper sql syntax. How can we test FromSql properly?
No error came from result of bad syntax .
seledg24g5ct * frofhm dbo.Product
Full Code:
namespace Tests.Services
{
public class ProductTest
{
private const string InMemoryConnectionString = "DataSource=:memory:";
private SqliteConnection _connection;
protected TestContext testContext;
public ProductServiceTest()
{
_connection = new SqliteConnection(InMemoryConnectionString);
_connection.Open();
var options = new DbContextOptionsBuilder<TestContext>()
.UseSqlite(_connection)
.Options;
testContext= new TestContext(options);
testContext.Database.EnsureCreated();
}
[Fact]
public async Task GetProductByIdShouldReturnResult()
{
var productList = testContext.Product
.FromSql($"seledg24g5ct * frofhm dbo.Product");
Assert.Equal(1, 1);
}
Using Net Core 3.1
There are two things to be taken into consideration here.
First, FromSql method is just a tiny bridge for using raw SQL queries in EF Core. No any validation/parsing of the passed SQL string occurs when the method is called except finding the parameter placeholders and associating db parameters with them. In order to get validated, it has to be executed.
Second, in order to support query composition over the FromSql result set, the method returns IQueryable<T>. Which means it is not executed immediately, but only if/when the result is enumerated. Which could happen when you use foreach loop over it, or call methods like ToList, ToArray or EF Core specific Load extension method, which is similar to ToList, but without creating list - the equivalent of foreach loop w/o body, e.g.
foreach (var _ in query) { }
With that being said, the code snippet
var productList = testContext.Product
.FromSql($"seledg24g5ct * frofhm dbo.Product");
does basically nothing, hence does not produce exception for invalid SQL. You must execute it using one of the aforementioned methods, e.g.
productList.Load();
or
var productList = testContext.Product
.FromSql($"seledg24g5ct * frofhm dbo.Product")
.ToList();
and assert the expected exception.
For more info, see Raw SQL Queries and How Queries Work sections of EF Core documentation.
#ivan-stoev has answered your question as to why your '.FromSql' statement does nothing - i.e. the query is never actually materialized. But to try and add some additional value, i'll share my Unit Test setup as it works well for me. Of course, YMMV.
Create a reusable class to handle generic In-memory database creation and easy population of tables with test data. NB: this requires the Nuget packages:
ServiceStack.OrmLite.Core
ServiceStack.OrmLite.Sqlite
I am using OrmLite as it allows for mocking and unit testing by providing a non-disposing connection factory which I can neatly inject into the Test classes via Dependency Injection:
/// <summary>
/// It is not possible to directly mock the Dapper commands i'm using to query the underlying database. There is a Nuget package called Moq.Dapper, but this approach doesnt need it.
/// It is not possible to mock In-Memory properties of a .NET Core DbContext such as the IDbConnection - i.e. the bit we actually want for Dapper queries.
/// for this reason, we need to use a different In-Memory database and load entities into it to query. Approach as per: https://mikhail.io/2016/02/unit-testing-dapper-repositories/
/// </summary>
public class TestInMemoryDatabase
{
private readonly OrmLiteConnectionFactory dbFactory =
new OrmLiteConnectionFactory(":memory:", SqliteDialect.Provider);
public IDbConnection OpenConnection() => this.dbFactory.OpenDbConnection();
public void Insert<T>(IEnumerable<T> items)
{
using (var db = this.OpenConnection())
{
db.CreateTableIfNotExist<T>();
foreach (var item in items)
{
db.Insert(item);
}
}
}
}
A 'DbConnectionManager<EFContext>' class to provide the wrapper to the database connection using the EF Context you will already have created. This grabs the database connection from the EF Context and abstracts away the opening/closing operations:
public class DbConnectionManager<TContext> : IDbConnectionManager<TContext>, IDisposable
where TContext : DbContext
{
private TContext _context;
public DbConnectionManager(TContext context)
{
_context = context;
}
public async Task<IDbConnection> GetDbConnectionFromContextAsync()
{
var dbConnection = _context.Database.GetDbConnection();
if (dbConnection.State.Equals(ConnectionState.Closed))
{
await dbConnection.OpenAsync();
}
return dbConnection;
}
public void Dispose()
{
var dbConnection = _context.Database.GetDbConnection();
if (dbConnection.State.Equals(ConnectionState.Open))
{
dbConnection.Close();
}
}
}
Accompanying injectable Interface for the above:
public interface IDbConnectionManager<TContext>
where TContext : DbContext
{
Task<IDbConnection> GetDbConnectionFromContextAsync();
void Dispose();
}
In your .NET Project Startup class, register this interface with the inbuilt DI container (or whatever one you're using):
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped(typeof(IDbConnectionManager<>), typeof(DbConnectionManager<>));
}
Now our Unit Test class looks like this:
/// <summary>
/// All tests to follow the naming convention: MethodName_StateUnderTest_ExpectedBehaviour
/// </summary>
[ExcludeFromCodeCoverage]
public class ProductTests
{
//private static Mock<ILoggerAdapter<Db2DbViewAccess>> _logger;
//private static Mock<IOptions<AppSettings>> _configuration;
private readonly Mock<IDbConnectionManager<Db2Context>> _dbConnection;
private readonly List<Product> _listProducts = new List<Product>
{
new Product
{
Id = 1,
Name = "Product1"
},
new Product
{
Id = 2,
Name = "Product2"
},
new Product
{
Id = 3,
Name = "Product3"
},
};
public ProductTests()
{
//_logger = new Mock<ILoggerAdapter<Db2DbViewAccess>>();
//_configuration = new Mock<IOptions<AppSettings>>();
_dbConnection = new Mock<IDbConnectionManager<Db2Context>>();
}
[Fact]
public async Task GetProductAsync_ResultsFound_ReturnListOfAllProducts()
{
// Arrange
// Using a SQL Lite in-memory database to test the DbContext.
var testInMemoryDatabase = new TestInMemoryDatabase();
testInMemoryDatabase.Insert(_listProducts);
_dbConnection.Setup(c => c.GetDbConnectionFromContextAsync())
.ReturnsAsync(testInMemoryDatabase.OpenConnection());
//_configuration.Setup(x => x.Value).Returns(appSettings);
var productAccess = new ProductAccess(_configuration.Object); //, _logger.Object, _dbConnection.Object);
// Act
var result = await productAccess.GetProductAsync("SELECT * FROM Product");
// Assert
result.Count.Should().Equals(_listProducts.Count);
}
}
Notes on the above:
You can see i'm testing a 'ProductAccess' Data Access class which wraps my database calls but that should be easy enough to change for your setup. My ProductAccess class is expecting other services such as logging and Configuration to be injected in, but i have commented these out for this minimal example.
Note the setup of the in-memory database and populating it with your test list of entities to query is now a simple 2 lines (you could even do this just once in the Test class constructor if you want the same test dataset to use across tests):
var testInMemoryDatabase = new TestInMemoryDatabase();
testInMemoryDatabase.Insert(_listProducts);

Unit Testing a controller that uses windows authentication

-------Please see updates below as I now have this set up for dependency injection and the use of the MOQ mocking framework. I'd still like to split up my repository so it doesn't directly depend on pulling the windowsUser within the same function.
I have a Web API in an intranet site that populates a dropdown. The query behind the dropdown takes the windows username as a parameter to return the list.
I realize I don't have all of this set up correctly because I'm not able to unit test it. I need to know how this "should" be set up to allow unit testing and then what the unit tests should look like.
Additional info: this is an ASP.NET MVC 5 application.
INTERFACE
public interface ITestRepository
{
HttpResponseMessage DropDownList();
}
REPOSITORY
public class ExampleRepository : IExampleRepository
{
//Accessing the data through Entity Framework
private MyDatabaseEntities db = new MyDatabaseEntities();
public HttpResponseMessage DropDownList()
{
//Get the current windows user
string windowsUser = HttpContext.Current.User.Identity.Name;
//Pass the parameter to a procedure running a select query
var sourceQuery = (from p in db.spDropDownList(windowsUser)
select p).ToList();
string result = JsonConvert.SerializeObject(sourceQuery);
var response = new HttpResponseMessage();
response.Content = new StringContent(result, System.Text.Encoding.Unicode, "application/json");
return response;
}
}
CONTROLLER
public class ExampleController : ApiController
{
private IExampleRepository _exampleRepository;
public ExampleController()
{
_exampleRepository = new ExampleRepository();
}
[HttpGet]
public HttpResponseMessage DropDownList()
{
try
{
return _exampleRepository.DropDownList();
}
catch
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
}
}
}
UPDATE 1
I have updated my Controller based on BartoszKP's suggestion to show dependency injection.
UPDATED CONTROLLER
public class ExampleController : ApiController
{
private IExampleRepository _exampleRepository;
//Dependency Injection
public ExampleController(IExampleRepository exampleRepository)
{
_exampleRepository = exampleRepository;
}
[HttpGet]
public HttpResponseMessage DropDownList()
{
try
{
return _exampleRepository.DropDownList();
}
catch
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
}
}
}
UPDATE 2
I have decided to use MOQ as a mocking framework for unit testing. I'm able to test something simple, like the following. This would test a simple method that doesn't take any parameters and doesn't include the windowsUser part.
[TestMethod]
public void ExampleOfAnotherTest()
{
//Arrange
var mockRepository = new Mock<IExampleRepository>();
mockRepository
.Setup(x => x.DropDownList())
.Returns(new HttpResponseMessage(HttpStatusCode.OK));
ExampleController controller = new ExampleController(mockRepository.Object);
controller.Request = new HttpRequestMessage();
controller.Configuration = new HttpConfiguration();
//Act
var response = controller.DropDownList();
//Assert
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
I need help testing the DropDownList method (one that does include code to get the windowsUser). I need advice on how to break this method apart. I know both parts shouldn't been in the same method. I don't know how to arrange splitting out the windowsUser variable. I realize this really should be brought in as a parameter, but I can't figure out how.
You usually do not unit-test repositories (integration tests verify if they really persist the data in the database correctly) - see for example this article on MSDN:
Typically, it is difficult to unit test the repositories themselves, so it is often better to write integration tests for them.
So, let's focus on testing only the controller.
Change the controller to take IExampleRepository in its constructor as a parameter:
private IExampleRepository _exampleRepository;
public ExampleController(IExampleRepository exampleRepository)
{
_exampleRepository = exampleRepository;
}
Then, in your unit tests, use one of mocking frameworks (such as RhinoMock for example) to create a stub for the sole purpose of testing the controller.
[TestFixture]
public class ExampleTestFixture
{
private IExampleRepository CreateRepositoryStub(fake data)
{
var exampleRepositoryStub = ...; // create the stub with a mocking framework
// make the stub return given fake data
return exampleRepositoryStub;
}
[Test]
public void GivenX_WhenDropDownListIsRequested_ReturnsY()
{
// Arrange
var exampleRepositoryStub = CreateRepositoryStub(X);
var exampleController = new ExampleController(exampleRepositoryStub);
// Act
var result = exampleController.DropDownList();
// Assert
Assert.That(result, Is.Equal(Y));
}
}
This is just a quick&dirty example - CreateRepositoryStub method should be of course extracted to some test utility class. Perhaps it should return a fluent interface to make the test's Arrange section more readable on what is given. Something more like:
// Arrange
var exampleController
= GivenAController()
.WithFakeData(X);
(with better names that reflect your business logic of course).
In case of ASP.NET MVC, the framework needs to know how to construct the controller. Fortunately, ASP.NET supports the Dependency Injection paradigm and a parameterless constructor is not required when using MVC unity.
Also, note the comment by Richard Szalay:
You shouldn't use HttpContext.Current in WebApi - you can use base.User which comes from HttpRequestBase.User and is mockable. If you really want to continue using HttpContext.Current, take a look at Mock HttpContext.Current in Test Init Method
One trick that I find very useful when trying to make old code testable when said code is accessing some global static or other messy stuff that I can't easily just parameterize is to wrap access to the resource in a virtual method call. Then you can subclass your system under test and use that in the unit test instead.
Example, using a hard dependency in the System.Random class
public class Untestable
{
public int CalculateSomethingRandom()
{
return new Random().Next() + new Random().Next();
}
}
Now we replace var rng = new Random();
public class Untestable
{
public int CalculateSomethingRandom()
{
return GetRandomNumber() + GetRandomNumber();
}
protected virtual int GetRandomNumber()
{
return new Random().Next();
}
}
Now we can create a testable version of the class:
public class Testable : Untestable
{
protected override int GetRandomNumber()
{
// You can return whatever you want for your test here,
// it depends on what type of behaviour you are faking.
// You can easily inject values here via a constructor or
// some public field in the subclass. You can also add
// counters for times method was called, save the args etc.
return 4;
}
}
The drawback with this method is that you can't use (most) isolation frameworks to implement protected methods (easily), and for good reason, since protected methods are sort of internal and shouldn't be all that important to your unit tests. It's still a really handy way of getting things covered with tests so you can refactor them, instead of having to spend 10 hours without tests, trying to do major architectual changes to your code before you get to "safety".
Just another tool to keep in mind, I find it comes in handy from time to time!
EDIT: More concretely, in your case you might want to create a protected virtual string GetLoggedInUserName(). This will technically speaking keep the actual call to HttpContext.Current.User.Identity.Name untested, but you will have isolated it to the simplest smallest possible method, so you can test that the code is calling the correct method the right amount of times with the correct args, and then you simply have to know that HttpContext.Current.User.Identity.Name contains what you want. This can later be refactored into some sort of user manager or logged in user provider, you'll see what suits best as you go along.

Testing CRUD class.

I have some issue. Im writing some unit test in my project but i don't know how to test my CRUD methods.. Maybe they are not testable ;/
This is one of my methods:
public static void IncrementInvalidLoginColumn(string login)
{
User user;
using (DTContext context = new DTContext())
{
try
{
user = context.Users.Where(u => u.Login.CompareTo(login) == 0).FirstOrDefault();
if (user.InvalidLogins < 3)
{
user.InvalidLogins = user.InvalidLogins + 1;
}
context.SaveChanges();
}
catch
{
}
}
}
Maybe someone will have idea what should i do.
It depends on what you mean by "unit" test. If you don't want your test to hit the database then your method is not testable (or at least not without some refactoring).
If hitting the database is acceptable (which would actually be an integration test) then you can definitely test your method.
Here are some steps:
1. Arrange the initial data. You use an instance of the DTContext directly in the test to put the system in a predefined state (basically you write some user records in the database)
You run the method you want to test (which in fact uses its own instance of the DTContext)
You use DTContext again to read the user information directly from the database and assert that the InvalidLogins property has incremented.
You need to make sure you delete any data that you put in manually.
This is the gist of DI:
public class Example {
private IDatabaseGateway myDatabase;
public Example(IDatabaseGateway myDb) {
myDatabase = myDb;
}
public void DoStuff() {
...
myDatabase.GetData();
...
}
}
You give your business class an abstraction of the database via the constructor, that is you inject your dependencies in the class that needs them.
Once you have this in place, in production code you pass in the constructor a concrete instance of IDatabaseGateway that goes to the actual database.
In the case of a unit test you pass it a mock instance of the same interface. The mock is a special object that you can setup/configure to return what you want. Various libraries exist for mocking (an easy one is Moq).
However without modifying your code too much, it is better to stick with integration testing that hits the database. It will give you a simple and valid test.
Especially since there are some pitfalls in mocking the DbContext in EF (ex. some queries may not work when you will use them in production, testing updates in EF with mocks is a bit trickier).
Ok so i read all of your posts and they was very helpful.
I use MOQ framework and this is example how i do it.
This is how Liviu M. told me to do for example:
public class CRUDclass
{
private DTContext _context;
public CRUDclass(DTContext myObj)
{
_context = myObj;
}
}
We have CRUD Class which are doing operations directly on our database. We have constructor with one argument and private field. This our context :)
This is (for example) my method in CRUDclass:
public bool AddUser(User user)
{
try
{
_context.Users.Add(user);
_context.SaveChanges();
return true;
}
catch
{
return false;
}
}
Ovecourse he have our DTContext class witch DBSet becouse i using entity framework. And after that i am able to write some test method:
[TestMethod]
public void Should_Add_User()
{
var mockSet = new Mock<DbSet<User>>();
var mockContext = new Mock<DTContext>();
mockContext.Setup(m => m.Users).Returns(mockSet.Object);
var usrCRUD = new UserCRUD(mockContext.Object);
var usr = new User();
usr.Login = "Login_Name";
usr.Email = "loginName#test.com";
usr.Password = "***";
usr.InvalidLogins = 0;
usr.RememberID = 0;
usrCRUD.AddUser(usr);
mockSet.Verify(m => m.Add(It.Is<User>(arg => arg.Login == "Login_Name")));
mockContext.Verify(m => m.SaveChanges(), Times.Once());
}
At first a have to set my fake object (Mock>).
This test method checks if our user was added to Mock :)
I hope it can help somebody, if anything will be unclear please write a question :)
The idea of unit tests is to test your ifs, switches, etc., not the database operations.
In your case you need an interface that is an abstration of DTContext. In the simplest case it might look as the following.
public interface IObjectContext : IDisposable
{
IEnumerable<User> Users { get; }
}
In more complicated cases you may need to use IQueryable<T> or IObjectSet<T> instead of IEnumerable<T>.
Add a partial class declaration of DTContext and make it implement IObjectContext. Add a constructor to the class that contains the method IncrementInvalidLoginColumn with a parameter of type IObjectContext. Now you can inject any instance of IObjectContext instead of creating it in your class. This instance can be a DTContext or a mock for testing. Your class is ready to be tested without connection to a real database.
NB. In case of instances of IDisposable it's better to inject a Func<IObjectContext> instead of IObjectContext. Then you can create an instance for each operation and dispose it immediately after.
If there are CRUD operations in your code then I will recommend to use MOQ framework for unit testing. Below links are quite helpful:
Quick Start
Code Project
Ideally you would inject your DTContext rather than creating a new one every time that the method is called. That way you could mock that object in your unit test and verify that it is called as expected.
Your constructor would look something like:
private readonly IDTContext _context;
public CrudClass(IDTContext context)
{
_context = context
}
With your method now looking like
public static void IncrementInvalidLoginColumn(string login)
{
User user;
try
{
user = _context.Users.Where(u => u.Login.CompareTo(login) == 0).FirstOrDefault();
if (user.InvalidLogins < 3)
{
user.InvalidLogins = user.InvalidLogins + 1;
}
_context.SaveChanges();
}
catch
{
// Handle errors
}
}
And then in your test, if you were using a framework like Moq, you would basically script how that object would behave and test against that. For instance, setting up the mocked IDTContext to always return the same user for your Users collection and SaveChanges() method will write the number of invalid logins to a variable that you could then test against.

Approach to test public methods depending on private methods

I'm trying to add tests to a legacy code, and as I start to adding code, I get the feeling that something is wrong.
In the following code, the public method RegisterChange is calling two private methods to:
Get the object to store
Store the object
public class ChangeService {
IRepository repository;
public ChangeService(IRepository repository){
this.repository = repository;
}
public bool RegisterChange( int entityId ){
var entity = GetParsedEntity( entityId );
SaveEntity( entity );
return true;
}
private Entity GetParsedEntity( int id ) {
var entity = repository.GetEntityById( id );
return new Entity{ Name = entity.Name };
}
private void SaveEntity( Entity entity ) {
repository.Save( Entity );
}
}
public class ChangeServiceFact(){
[Fact]
public void When_valid_entity__Should_save_entity(){
var mock = new Mock<IRepository>();
var service = new ChangeService(mock.object);
var result = service.RegisterChange( 0 );
Assert.True(result);
}
}
So, when Im mocking the repository, I had to go and check the private method's code to know which operations to mock.
The problem that I'm seeing with this approach is that, because the code is testing not only the test subject (the public method) but also the private methods, is not clear which should be the test result by looking at the test subject (public method).
In the case that, later on, someone decide to modify one private method (like throwing an exception from GetParsedEntity), the test will continue to pass correctly, but the client code could fail because of this change.
In this particular case, Im using C#, XUnit and Moq, but I think is more a general testing question.
The problem that I'm seeing with this approach is that, because the code is testing not only the test subject (the public method) but also the private methods, is not clear which should be the test result by looking at the test subject (public method).
The test subject you mention has no visible effect without knowing its full contract. What the full contract here is? The mentioned public method and constructor, which takes dependency. It's the dependency that's important here and interaction with this dependency is what should be tested. Private methods are (as always) implementation detail - irrelevant to unit testing.
Having said that, let's get back to the contract. What is the actual contract of test subject (ChangeService method)? To retrieve object from repository basing on some id, create different object and save the later in the same repository. And this is your test.
[Fact]
public void ChangeService_StoresNewEntityInRepository_BasedOnProvidedId()
{
const string ExpectedName = "some name";
var otherEntity = new OtherEntity { Name = ExpectedName };
var mock = new Mock<IRepository>();
var service = new ChangeService(mock.object);
mock.Setup(m => m.GetEntityById(0)).Return(otherEntity);
service.RegisterChange(0);
mock.Verify(m => m.SaveEntity(It.Is<Entity>(e => e.Name == ExpectedName));
}

fake DataRepository - emulating the database

Quick Info: I'm using C# 4.0 and RhinoMocks (with AAA)
I'll explain with some code what I'm thinking about doing:
public class SampleData
{
private List<Person> _persons = new List<Person>()
{
new Person { PersonID = 1, Name = "Jack"},
new Person { PersonID = 2, Name = "John"}
};
public List<Person> Persons
{
get { return _persons; }
}
}
So this is a class which mimics data in the DB. Now I want to use this data in my unit tests. In other words, instead of getting data out of the DB, I want to get them out of the datarepository.
I think I can achieve this by stubbing the Repository and by making it use the DataRepository instead:
UC1003_ConsultantsBeherenBL consultantsBeherenBL = new UC1003_ConsultantsBeherenBL();
consultantsBeherenBL = MockRepository.GeneratePartialMock<UC1003_ConsultantsBeherenBL>();
consultantsBeherenBL.Repository = MockRepository.GenerateMock<IRepository>();
This would cause my code to automaticly look for data in the DataRepository instead. So instead of stubbing a method and directly inserting a list (e.g. d => d.Find(Arg.Is.Anything)).IgnoreArguments().Return(a list which you just filled up)) I'd get "real" data back (the data which has been filtered from the DataRepository). This means I can test if my code can really find something, without having to insert test data in my DB (integration test).
How would I go about implementing such a thing? I've tried looking on the web for articles or questions, but I can't seem to find a lot :/
Any help is appreciated.
EDIT: I've tried to SimpleInjector and StructureMap, but I'm stuck implementing one of them.
I'm currently using a repository on my entity framework, so my baseBL looks like this (note: all my other BL's enherit from this one):
public class BaseBL
{
private IRepository _repository;
public IRepository Repository
{
get
{
if (_repository == null)
_repository = new Repository(new DetacheringenEntities());
return _repository;
}
set { _repository = value; }
}
public IEnumerable<T> GetAll<T>()
{
... --> Generic methods
My Repository class:
public class Repository : BaseRepository, IRepository
{
#region Base Implementation
private bool _disposed;
public Repository(DetacheringenEntities context)
{
this._context = context;
this._contextReused = true;
}
#endregion
#region IRepository Members
public int Add<T>(T entity)
... --> implementations of generic methods
As far as I can make out, I now need to be able to say in my tests that instead of using the DetacheringenEntities, I need to use my DataRepository. I don't understand how I switch out my entity framework with a data class, because that data class won't fit in there.
Should I let my DataRepository enherit my IRepository class and make my own implementations?
public class SampleData : IRepository
But I can't do things like this with my lists :/
public IEnumerable<T> GetAll<T>()
{
return Repository.GetAll<T>();
}
Big thanks again for help
EDIT: I realised that a unit test doesn't need a data repository, so I'm just testing that logic in an integration test. This makes a data repository useless, since the code can be tested without a repository.
I'd like to thank everybody for their help though, thanks :)
Use a Dependency injection framework to handle your dependencies. In your unit test you can swap the real implementation with a stubbed one.
In StructureMap for example, you'll say in your code. "All right, now give me the active instance of IDataRepository", for your normal code this would point to an implementation to the real database. In your unittest you can then overwrite this by putting ObjectFactory.Inject(new FakeDataRepository()). The fake repo is then used by all your code, which makes it really easy to test a single unit of work.Í

Categories