当前位置: 首页 > 知识库问答 >
问题:

模拟测试不返回任何值JavaJUnit

董哲
2023-03-14

我正在尝试测试一种使用hibernate、junit和mocking连接到db的方法
这是我的代码

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

测试用例向db添加了一组用户名和密码,但当我尝试使用getUsers()返回结果时,它会返回一个空列表。

有人能帮我解决这个问题吗?

共有2个答案

鲁昕
2023-03-14

建议:后退一步。现在

当您首先缺乏对如何编写单元测试的基本了解时,使用JUnit和Mockito来测试hibernate和DOA是没有意义的。在您的代码中:

@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());
}

几乎没有什么是有意义的。典型的单元测试更像是:

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

笔记:

  • 你不会嘲笑你想要测试的类。您可以模拟被测类完成其工作所需的对象;但是,您仍然只模拟那些必须模拟才能通过测试的对象
  • 然后对测试对象调用一个方法;你要么断言某种预期的结果回来了;或者使用模拟验证来检查对模拟对象的预期调用是否发生

长话短说:你应该花几天时间阅读关于JUnit和Mockito的教程。换言之:在跨栏跑之前要学会爬行!

慕容典
2023-03-14

首先,您没有向DB中添加任何用户,因为userDAOImpl是一个模拟对象,因此,正如Joe C所指出的,addUser方法永远不会在真实对象上调用。出于同样的原因(userDAOImpl)getUsers方法不会返回任何列表。

正如您已经告诉会话工厂(sessionFactory)要做什么,然后调用它的方法(getCurrentSession())一样,您可以告诉用户daoimpl(userDAOImpl)和getUsers(getUsers)在调用它的方法时要做什么。

另请注意:testCreate()方法不应包含系统。出来println方法,因为JUnit无法知道测试应该通过还是失败。如果使用的是Mockito,则可以使用verify方法来确保执行某些代码行。

或者,如果要测试存储库,可以使用内存中的数据库并创建真实的对象来向数据库中插入数据和从数据库中读取数据。这样,主数据库就不会被测试数据污染。这里有一篇关于内存测试数据库的好文章。

更新:使用Mockito测试UserDAOImpl

我做的第一件事是,稍微更改了UserDAOImpl类。原因是:你不能用Mockito来模拟静态方法(至少在写这篇文章的时候是这样)。这里有更多信息。

我将会话对象传递给UserDAOImpl,并使用该会话来开始事务,而不是使用静态方法DbUtils。

下面是修改后的UserDAOImpl类:

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

让我们看看如何使用模拟对象测试UserDAOImpl的方法。最初,我们模拟我们没有测试的对象,但我们需要它们来执行被测试代码的语句。

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

注意:我们没有测试会话,也没有测试事务;因此,我们使用Mockito将这些对象提供给我们的UserDAOImpl类。我们假设方法save(User User)commit()以及模拟对象的其他方法工作正常,因此我们只测试addUser方法。使用Mockito,我们不需要建立到数据库的真正会话,而是模拟对象,这更简单、更快(此外,即使我们还没有开发代码的其他部分,也可以测试addUser方法-其他一些开发人员可能正在进行这方面的工作)。

在上面的测试用例中,addUserTest()方法测试当模拟对象的方法按预期的方式运行时,该方法是否正确执行。通过使用验证(session.getTransaction())。commit()我们确保调用了commit()方法,否则在catch块中测试失败。

同样,我们可以测试在抛出异常时是否回滚事务。您可以这样做:

@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();
    }
}

这个测试用例测试addUser方法,如果它在保存更改时抛出异常时表现为异常。当使用这段代码doThrow(new SQLGrammarException())调用save方法时,可以通过告诉模拟对象抛出异常来模拟异常。何时(会话)。保存(any()) verify(transaction,never())调用commit()方法。提交() 。在catch(捕获)块中,验证事务是否已回滚:verify(事务)。回滚()

下面是包含这两个测试用例的完整类:

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();
        }
    }
}
 类似资料:
  • 问题内容: 我正在使用pythons mock.patch并想更改每个调用的返回值。请注意,正在修补的函数没有输入,因此无法基于输入更改返回值。 这是我的代码供参考。 我的测试代码: 只是“ input”的独立于平台的版本(python 2和3)。因此,最终我将尝试模拟用户的输入。我已经尝试过使用列表作为返回值,但这并不能正常工作。 您可以看到,如果返回值无效,那么我将在此处得到一个无限循环。因此

  • 我正在尝试对服务类进行单元测试,但模拟返回null 在上面的代码中,当调用到达服务类时为空。我不知道我是否错过了什么。我尝试使用和运行,但结果相同。感谢任何帮助。 提前谢谢你。

  • 问题内容: 我的组件测试文件中有一个像这样的模拟模块 这些函数将在我的组件的渲染函数中调用以隐藏和显示某些特定功能。 我想对这些模拟函数的返回值的不同组合进行快照。 假设我有一个这样的测试用例 要运行此测试用例,我想更改模拟模块函数的返回值以使其动态变化 因为我已经一次导入了组件,所以我的模拟模块不会再次重新导入。所以它不会改变。我该如何解决? 问题答案: 您可以模拟该模块,以便它返回间谍并将其导

  • 我还没有看到这个问题的答案能解决我的问题。 我有一个服务类,它是用一个存储库和一个Orika MapperFacade构建的。在测试中,我正在安排存储库调用返回的实体列表。my doReturn(testList).when(testRepository).findAllBy...似乎正在工作。我的实际结果列表包含我在模拟列表中指定的相同数量的元素(.size()),但是这些值都是空的。我对Spr

  • 问题内容: 使用模拟进行单元测试conn(): app.py test.py 错误:未调用 断言 。 我认为这是因为’Database_url’不在我打补丁的os.environ中,并且因为没有对mysql_mock.connect进行该测试调用。 问题: 1为了使此测试代码有效,我需要进行哪些更改? 2.我还必须修补’urlparse’吗? 问题答案: 您可以尝试此解决方案。只需调用一个参数即可

  • 如何模拟返回已强制转换的模拟对象的方法。 试验方法。