2

While writing unit test, we always say that we need to ensure that code is always isolated from external dependencies. Below Moq has been used to provide a mocked object instead of a valid fluent nhibernate Session Factory.

  public class and_saving_a_invalid_item_type : 
  when_working_with_the_item_type_repository
  {
    private Exception _result;
    protected override void Establish_context()
    {
        base.Establish_context();
  _sessionFactory = new Mock<ISessionFactory>();
        _session = new Mock<ISession>();

       _sessionFactory.Setup(sf => sf.OpenSession()).Returns(_session.Object);         
        _itemTypeRepository = new ItemTypeRepository(_sessionFactory.Object);

       _session.Setup(s => s.Save(null)).Throws(new ArgumentNullException());
    }
    protected override void Because_of()
    {
        try
        {
            _itemTypeRepository.Save(null);
        }
        catch (Exception ex)
        {

            _result = ex;
        }

    }

    [Test]
    public void then_an_argument_null_exception_should_be_raised()
    {
        _result.ShouldBeInstanceOfType(typeof(ArgumentNullException));
    }
 }

The actual implementation is shown below. The test runs fine. But without the expectation set to throw argumentnullexception , the save method actually return NullReferenceException. The point is: isn't the unit test obscuring the actual result. Though the requirement is fulfilled from unit testing point of view , it is not fulfilled actually while implemented.

public class ItemTypeRepository : IItemTypeRepository
{
public int Save(ItemType itemType)
    {
        int id;
        using (var session = _sessionFactory.OpenSession())
        {
            id = (int) session.Save(itemType);                
            session.Flush();
        }
        return id;
    }
}
arjun
  • 625
  • 10
  • 27

2 Answers2

2

You are right since it is only NHibernate that gets tested in this example, and (edit: NHibernate's not tested either) that the test doesn't do much. Even if the query is complex, most people choose to write an integration test against an in-memory database (or a real one) since it would test the same path and would be written anyway. Mocking the database usually returns little on the investment compared to integration tests.

These links can also help:

Update: This repository in the example surely has some purpose in life, for example it is going to be used by this class:

class ItemUser {
   public ItemUser(IItemRepository repository) {}
}

My point is: testing ItemUser with a moq instance of IItemRepository is more fruitful. Whether or not the item is really saved will be tested in the integration tests.

Community
  • 1
  • 1
henginy
  • 2,041
  • 1
  • 16
  • 27
  • Then what is the purpose of unit testing by isolation using mock framework and all if ultimately we need to write integration testing. – arjun Nov 24 '13 at 04:02
  • You can mock out the repository without mocking ISessionFactory; for example: just test the logic, pretend the item is saved in the repository; that's what's going to be tested in the integration tests. Please see the update. – henginy Nov 24 '13 at 04:11
2

The test doesn't make a lot of sense because your are basically just testing that the mock returns an exception. You are not testing nhibernate, therefore the test has no real value.

The only unit test which would make sense is testing that session.Save(itemType) is called with whatever you pass in AND session.Flush(); gets called afterwards! This would validate that this part of code is always doing exactly that...

There are some fundamental differences between unit testing business logic and integration testing data access.

Your example is a typical data access class. You actually just wrap some nhibernate logic with your own Save method...

To test nhibernate's very own behavior, you have to do integration testing with some kind of database and without mocking out nhibernate.

Anyways, one small thing regarding the code you posted. You should actually check against null yourself at that point because the method is public and you expect some value not being null. Don't let nhibernate fail later. Simply do a null check. And unit test that.

MichaC
  • 13,104
  • 2
  • 44
  • 56