Tuesday, December 9, 2014

Unit testing - How to verify dependent object's methods are called?

Testing void methods

In one of previous post, we saw what is mocking in unit testing. If the function which we are going to unit test is returning a value also expecting some value from the dependency which we mocked, its easy to test. But how to test a method which doesn't return a value, but successful execution calls a method in the dependency. In other sense we need to know whether a particular method in mock gets called or not to determine the test result.

So this post is aiming at how to test void methods which calls a particular method in dependency. Hope the purpose on why we should check whether a method in dependency is called or not is clear.

If its not clear, lets take one scenario. Sorry my project mates felt it difficult to understand so I am thinking there would be similar kind of developers out there. Others can skip the below story and look at the code.

Take the scenario of saving a Person object into database. There is a PersonRepository class with Save() method. It needs to validate some business rules such as the phone number and email. When we test the void PersonRepository.Save() by writing unit test cases, we must be focused only on the logic inside the Save(), not about saving into database. That will be done by IDataAccess which is a dependency of PersonRepository. We need to make sure that the Save() method is calling IDataAccess.InsertOrUpdate() upon success

Below is the code snippet which is ensuring that a method of dependency is called from production code upon success. It uses Moq library.

    public class PersonRepository
    {
        IDataAccess _dataAccess;
        public PersonRepository(IDataAccess dataAccess)
        {
            _dataAccess = dataAccess;
        }
        public void Save(Person person)
        {
            ThrowExceptionIfInvalid(person);
            _dataAccess.InsertOrUpdate(person);
        }
        private void ThrowExceptionIfInvalid(Person person)
        {
            if(string.IsNullOrEmpty(person.EMail))
            {
            throw new ArgumentNullException("e-Mail should not be empty");
            }
            //More code goes here to validate email & phone
        }
    }

Unit test code

        [TestMethod()]
        public void WhenEverythingIsOK_CallIDataAccessSave()
        {
            Mock<IDataAccess> mockDataAccess = new Mock<IDataAccess>();
 
            Person person = new Person() { EMail = "joymon@gmail.com" };
            PersonRepository repository = new PersonRepository(mockDataAccess.Object);
            repository.Save(person);//Test only the logic written inside Save() method.

            //IDataAccess.InserOrUpdate() will be called on success
            mockDataAccess.Verify(mock => mock.InsertOrUpdate(person), Times.Exactly(1));
        }

Happy testing.

No comments: