Newbie
Newbie

Reputation: 2708

Mock test not returning any value Java Junit

I am trying to test a method that connects to db using hibernate using junit and mocking.
Here is my code

UserDAO.java

public interface UserDAO {    
    public void addUser(String username, String password);
    public List<String> getUsers();
}

UserDAOImpl.java

public class UserDAOImpl implements UserDAO {
    public static final Logger LOG = LoggerFactory.getLogger(UserDAOImpl.class);
    private static Session session;

    public UserDAOImpl() {      
    }

   public UserDAOImpl(Session session) {
     this.session = session;
   }
    private static void beginSession() {
        session = DbUtils.getSessionFactory().openSession();
        session.beginTransaction();
    }

    @Override
    public void addUser(String username, String password) {
        String encryptedPassword = Utils.encrypt(password);
        User user = new User(username, encryptedPassword);
        beginSession();
        try {
            session.save(user);
            System.out.println(user.getPassword());
            session.getTransaction().commit();
        } catch (SQLGrammarException e) {
            session.getTransaction().rollback();
            LOG.error("Cannot save user", e);
        } finally {
            session.close();
        }
    }

    @Override
    public List<String> getUsers() {
        beginSession();
        List<String> results = new ArrayList<String>();
        String hql = "select username from User";
        Query query = null;
        try {
            query = session.createQuery(hql);
            results = query.list();
        } catch (HibernateException e) {
            LOG.error("Cannot execute query", e);
        } 
        return results;
    }
}

TestUserDAOImpl

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import static org.mockito.Mockito.*;
import org.mockito.runners.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class TestUserDAOImpl {

    @Mock
    SessionFactory sessionFactory;

    @Mock 
    Session session;

    @Before
    public void setup() {
      when(sessionFactory.getCurrentSession()).thenReturn(session);
    }

    @Test
    public void testCreate() {
        // userDAOImpl = new UserDAOImpl(session);
        UserDAOImpl userDAOImpl = Mockito.mock(UserDAOImpl.class);
        String username = "username";
        String password = "password";
        userDAOImpl.addUser(username, password);
        System.out.println(userDAOImpl.getUsers());
    }
}

The test case adds a set of username and password to the db but when I try to return the results using getUsers() it returns a null list.

Can anyone please help me fix this?

Upvotes: 1

Views: 2406

Answers (2)

ahoxha
ahoxha

Reputation: 1938

First off, you are not adding any users to the DB, because userDAOImpl is a mocked object, therefore, as Joe C has pointed out, the addUser method never gets called on a real object. For the same reason (userDAOImpl is mocked) the getUsers method doesn't return any list.

Just as you have told sessionFactory what to do then its getCurrentSession() method is called, you can tell userDAOImpl what to do when its methods addUser and getUsers get called.

As a side note: the testCreate() method should not contain a System.out.println method, because JUnit cannot know whether your test should pass or fail. If you are using Mockito, you can use the verify method to make sure certain lines of code are getting executed.

Alternatively, if you want to test your repository, you can use an in-memory database and create real objects to insert data into and read from the database. This way your main database doesn't get polluted with test data. Here's a good article on in-memory test databases.


UPDATE: Testing the UserDAOImpl class using Mockito

The first thing I did is, changed the UserDAOImpl class a bit. The reason being: you can't mock static methods using Mockito (at least not at the moment of writing this post). More on this here.

I'm passing the session object to UserDAOImpl and using that session to begin the transaction, instead of using the static method of DbUtils.

Here's the modified UserDAOImpl class:

package test.mockito;

import java.util.ArrayList;
import java.util.List;

public class UserDAOImpl implements UserDAO {
    public static final Logger LOG = LoggerFactory.getLogger(UserDAOImpl.class);
    private Session session;

    public UserDAOImpl(Session session) {
        this.session = session;
    }

    @Override
    public void addUser(String username, String password) {
        String encryptedPassword = Utils.encrypt(password);
        User user = new User(username, encryptedPassword);
        session.beginTransaction();
        try {
            session.save(user);
            System.out.println(user.getPassword());
            session.getTransaction().commit();
        } catch (SQLGrammarException e) {
            session.getTransaction().rollback();
            if(LOG != null)LOG.error("Cannot save user", e);
        } finally {
            session.close();
        }
    }

    @Override
    public List<String> getUsers() {
        session.beginTransaction();
        List<String> results = new ArrayList<String>();
        String hql = "select username from User";
        Query query = null;
        try {
            query = session.createQuery(hql);
            results = query.list();
        } catch (HibernateException e) {
            if(LOG != null)LOG.error("Cannot execute query", e);
        }
        return results;
    }
}

Let's see how you can test the methods of UserDAOImpl using mocked objects. Initially, we mock objects that we are not testing, but we need them in order to execute statements of the code under test.

package test.mockito;

import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class UserDAOImplTest {

    @Mock
    Session session;

    @Mock
    Transaction transaction;

    @Before
    public void setUp() {
        when(session.getTransaction()).thenReturn(transaction);
    }

    @Test
    public void addUserTest() {
        UserDAO userDAO = new UserDAOImpl(session);
        userDAO.addUser("testusername", "testpassword");
        try {
            verify(session).getTransaction();
            verify(session.getTransaction()).commit();
        } catch (SQLGrammarException e) {
            fail(e.getMessage());
        }
        verify(session).close();
    }
}

Note: we are not testing Session, neither are we testing Transaction; therefore we are providing these objects to our UserDAOImpl class using Mockito. We assume that methods save(User user), commit(), and other methods of mocked objects, are working properly, so we only test the addUser method. Using Mockito we don't need to establish a real session to the database, but mock the object instead, which is much easier and faster (moreover, it makes it possible to test the addUser method even if we haven't developed yet the other parts of the code - some other developer may be working on that).

In the test case above, the addUserTest() method, tests if this method executes properly when the methods of mocked objects behave as they are expected to. By using verify(session.getTransacion()).commit() we make sure that the commit() method is called, otherwise in the catch block the test fails.

In the same way we can test if addUser rolls the transaction back when an exception is thrown. Here's how you can do it:

@Test
public void addUserTestFails() {
    UserDAO userDAO = new UserDAOImpl(session);
    try {
        doThrow(new SQLGrammarException()).when(session).save(any());
        userDAO.addUser("testusername", "testpassword");
        verify(transaction, never()).commit();
    } catch (SQLGrammarException e) {
        verify(transaction).rollback();
    }
}

This test case, tests the addUser method if it behaves as excepted when there's an exception thrown while saving the changes. You simulate the exception by telling the mocked object to throw an exception when the save method is called using this piece of code doThrow(new SQLGrammarException()).when(session).save(any());, and then you make sure that the commit() method is never called using verify(transaction, never()).commit();. In the catch block you verify that the transaction is rolled back: verify(transaction).rollback().

Here's the complete class that contains these two test cases:

package test.mockito;

import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class UserDAOImplTest {

    @Mock
    Session session;

    @Mock
    Transaction transaction;

    @Before
    public void setUp() {
        when(session.getTransaction()).thenReturn(transaction);
    }

    @Test
    public void addUserTest() {
        UserDAO userDAO = new UserDAOImpl(session);
        userDAO.addUser("testusername", "testpassword");
        try {
            verify(session).getTransaction();
            verify(session.getTransaction()).commit();
        } catch (SQLGrammarException e) {
            fail(e.getMessage());
        }
        verify(session).close();
    }

    @Test
    public void addUserTestFails() {
        UserDAO userDAO = new UserDAOImpl(session);
        try {
            doThrow(new SQLGrammarException()).when(session).save(any());
            userDAO.addUser("testusername", "testpassword");
            verify(transaction, never()).commit();
        } catch (SQLGrammarException e) {
            verify(transaction).rollback();
        }
    }
}

Upvotes: 2

GhostCat
GhostCat

Reputation: 140457

Suggestion: step back. Now.

There is no point in using JUnit and Mockito for testing hibernate and DOAs ... when you lack the basic understanding how to write unit tests in the first place. In your code:

@Test
public void testCreate() {
  // userDAOImpl = new UserDAOImpl(session);
  UserDAOImpl userDAOImpl = Mockito.mock(UserDAOImpl.class);
  String username = "username";
  String password = "password";
  userDAOImpl.addUser(username, password);
  System.out.println(userDAOImpl.getUsers());
}

almost nothing makes sense. A typical unit test goes more like:

class UnderTest {
  Foo foo;
  UnderTest(Foo foo) { this.foo = foo; }

  int bar(String whatever) { return foo.bar(whatever); }
}

class UnderTestTest {
  @Mock
  Foo foo;

  UnderTest underTest;

  @Before
  public void setup() { underTest = new UnderTest(foo); }

  @Test
  public void testBar() {
    when(foo.bar(any()).thenReturn(5);
    assertThat(underTest.bar(), is(5);
  }
}

Notes:

  • You don't mock the class you intend to test. You mock those objects that your class under test needs to do its job; but then, you still mock only those objects that you have to mock to make the test pass.
  • You then call a method on your object under test; and you either assert that some expected result comes back; or you using mock verification to check that the expected calls on your mocked objects took place.

Long story short: you should rather spent a few days reading tutorials about JUnit and Mockito. In other words: learn to crawl before going for the hurdle race!

Upvotes: 1

Related Questions