1

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;
    }

    //...
}
1
  • Yes, mocking the session variable is the solution. Then you can verify if the calls to subsequent fluent methods are done correctly. What's the problem? Commented Sep 29, 2013 at 12:50

3 Answers 3

2

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...

Sign up to request clarification or add additional context in comments.

1 Comment

I mock the session using Moq and using a IDbService that exposes IQueryable<T> so I can unit test controllers that embraces the NHibernate session or even in your case your facades. I got the idea from @OdeToCode a while ago. However some people do not like this way of doing things
2

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.

6 Comments

We do the same thing pretty much and I think these tests are better anyway because they are closer to integration tests.
@ColeW this IS actually integration testing... But not unit testing. Unit testing shouldn't care about the data, you will just test the small unit (piece of code) and do not want to read/write anything from/to the database. So for propper unit testing, you have to mock your database. For integration testing, sure, you would have to set up a test database which gets populated for every test set...
@Ela I agree with your statement but who needs unit tests in this case if you are doing integration tests? Let's face it you can't get around doing integration tests anyway so why write 2 tests to essentially do the same thing. I started out the same path you were and ended up not mocking the database.
@ColeW I actually ended up doing the opposite ;) If you have a lot of business logic which might have multiple layers, factories and so on, only one of those layers actually need to access the database. All others can/should be tested without hitting a database because you just want to test some calculation method or what not. This topic here is actually a good example. He just wants to test the pagination functionality. To test all extreme cases, it might be easier to mock the data. But ok, lets say it depends ;)
@Ela I would probably agree that it is situational. In my scenarios I needed to do so much mocking that it was easier just to insert the data I needed rather than to mock out all the calls. But in simple scenarios like this it may be easier to do mocking.
|
2

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
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.