Reputation: 327
I am trying to create application using .net mvc 4 and fluent nhibernate.
I have created ProductsFacade
which is responsible for getting and inserting data to a database.
Method GetProductsByPageAndCategory
is used to get page of records from database. I want to write unit test which checks if pagination works well.
It's hard to do because pagination must be done in single QueryOver
query. I can't write separate method only fetching data, mock it and write separate method for pagination. So I need to mock database. I use moq tool for mocking.
Maybe anyone could give some tips on how to do it? Or any other alternative how to solve my problem?
public class ProductFacade {
//...
public virtual IList<Product> GetProductsByPageAndCategory(
string category,
int pageNumber,
int pageSize)
{
//IList<Product> products = ;
var productsQuery = _session.QueryOver<Product>();
if (category != null)
{
productsQuery.Where(p => p.Category == category);
}
IList<Product> products = productsQuery
.OrderBy(p => p.Id)
.Desc
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize)
.List<Product>();
return products;
}
//...
}
Upvotes: 1
Views: 3560
Reputation: 30813
inmemory database is much less code than mocking, easier to understand and closer to the real thing. It also makes sure your mappings are correct so no extra load save tests needed.
//for all tests
static Configuration config;
static ISessionFactory testSessionFactory;
config = Fluently.Configure()
.Database(SQLiteConfiguration.Standard.InMemory().ShowSql().FormatSql())
.Mappings(m => m.AutoMappings.Add(AutoMap.AssemblyOf<Foo>()) // add your mappings here
.BuildConfiguration();
testSessionFactory = config.BuildSessionFactory();
// in test
public ProductTests()
{
session = sf.OpenSession();
new SchemaExport(config).Execute(true, true, false, session.Connection, null);
}
private void InjectSampleData(params object[] objects)
{
foreach (var obj in objects)
{
session.Save(obj);
}
session.Flush();
session.Clear();
}
public void TestThis()
{
InjectSampleData(
new Product() { ... },
new Product() { ... },
new Product() { ... },
new Product() { ... },
);
var products = new ProductFacade(session).GetProductsByPageAndCategory(...);
// assert products match expected subcollection of Injected Data
}
Upvotes: 2
Reputation: 45232
Here's my alternative - don't mock the database.
In our test setup, on every developer's machine there must be a database with a given name (eg "CMS_AutoTests"). When the tests run it interacts with this database.
The TearDown
method that runs after each tests runs a stored procedure that clears every table, so each test starts with an empty database.
Upvotes: 2
Reputation: 13381
I also use moq for mocking NHibernate session, here is a very simple example of how to mock the NHibernate ISession
and ISessionFactory
.
var mockSession = new Mock<ISession>();
mockSession.Setup(r => r.Get<ExampleEntity>(It.IsAny<int>()))
.Returns(new ExampleEntity());
var mockSessionFactory = new Mock<ISessionFactory>();
mockSessionFactory.Setup(r => r.OpenSession())
.Returns(mockSession.Object);
var sessionFactory = mockSessionFactory.Object;
// inject mockSessionFactory.Object to your business layer...
// code where ever sessionFactory is used...
// OpenSession now returns the mocked session
using (var session = sessionFactory.OpenSession())
{
//Get of type ExampleEntity will always return whatever you define in your mock
var rs = session.Get<ExampleEntity>(1);
}
To use mocking for your business objects, you'll have to design it in a way that you can manually construct it so that it uses your mocked factory.
Usually this is easy if you use injection with Unity for example. With unity the constructor of your Business class might take the session or the factory or whatever... In this case, within your unit test your can construct the target manually and pass your mock into it...
Upvotes: 2