java - 这是有效的单元测试吗?

标签 java unit-testing junit mockito

(我知道Person的复数是People,我只是更喜欢Persons..)

我试图理解测试的本质。我有一个玩具项目,在这个玩具项目中我有以下类(class):

package biz.tugay.jpaExamples.dao;

import biz.tugay.jpaExamples.model.Person;
import biz.tugay.jpaExamples.service.EntityManagerService;

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import java.util.List;

public final class PersonDaoImpl {

    private EntityManagerService entityManagerService;

    public List<Person> getAll() {
        final EntityManager entityManager = entityManagerService.entityManager();
        final TypedQuery<Person> selectAllPersons = entityManager.createQuery("SELECT p FROM Person p", Person.class);
        final List<Person> persons = selectAllPersons.getResultList();
        entityManager.close();
        return persons;
    }

    public void setEntityManagerService(final EntityManagerService entityManagerService) {
        this.entityManagerService = entityManagerService;
    }
}

当我运行我的项目时,这个方法工作得很好。我向它注入(inject)一个 EntityManagerService,当我在 PersonDaoImpl 中调用 getAll() 时,我将获得 Persons从数据库。这是我对 EntityManagerService 的实现,这与我认为的问题无关:

package biz.tugay.jpaExamples.service;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public final class EntityManagerServiceByPersistenceUnitNameImpl implements EntityManagerService {

    private final EntityManagerFactory entityManagerFactory;

    public EntityManagerServiceByPersistenceUnitNameImpl(final String persistenceUnitName) {
        this.entityManagerFactory = Persistence.createEntityManagerFactory(persistenceUnitName);
    }

    @Override
    public EntityManager entityManager() {
        final EntityManager entityManager = entityManagerFactory.createEntityManager();
        return entityManager;
    }

    @Override
    public void shutDown() {
        entityManagerFactory.close();
    }
}

因为我正在尝试研究/理解单元测试,所以我想为 PersonDaoImpl 创建一个单元测试。在这里:

package biz.tugay.jpaExamples.dao;

import biz.tugay.jpaExamples.model.Person;
import biz.tugay.jpaExamples.service.EntityManagerService;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import java.util.ArrayList;
import java.util.List;

@RunWith(MockitoJUnitRunner.class)
public class PersonDaoImplTest {

    @InjectMocks
    private final PersonDaoImpl personDao = new PersonDaoImpl();

    @Mock
    private EntityManagerService entityManagerService;

    @Mock
    private EntityManager entityManager;

    @Mock
    private TypedQuery<Person> typedQuery;

    @Test
    public void testGetAll() throws Exception {
        // Given we have 1 person in the database..
        final List<Person> persons = new ArrayList<Person>();
        final Person person = new Person();
        person.setFirstname("Koray");
        person.setLastname("Tugay");
        persons.add(person);

        // Given entityManagerService returns a valid EntityManager
        Mockito.when(entityManagerService.entityManager()).thenReturn(entityManager);

        // Given entityManager creates a valid TypedQuery
        Mockito.when(entityManager.createQuery("SELECT p FROM Person p", Person.class)).thenReturn(typedQuery);

        // Given typedQuery returns the persons from the database when getResultList is called
        Mockito.when(typedQuery.getResultList()).thenReturn(persons);

        // When personDao.getAll is called..
        final List<Person> all = personDao.getAll();

        // Then the returned List must be of size 1.
        Assert.assertTrue(all.size() == 1);

        // And the person in the List returned from the typedQuery must be equal to the person in the initial List.
        final Person returnedPerson = all.get(0);
        Assert.assertTrue(returnedPerson.getFirstname().equals("Koray"));
        Assert.assertTrue(returnedPerson.getLastname().equals("Tugay"));
        Mockito.verify(entityManager).close();
    }
}

这是一个有效的单元测试吗?如果不是,是不是因为我在 PersonDaoImpl 类中的设计不好,我该如何正确地对该方法进行单元测试?

如果是,您能否阐明它真正测试的是什么以及它如何帮助我重构或让我确信我在做正确的事/我正在做我正在做的事情?

因为目前,我认为编写此测试绝对没有任何好处,但我很确定这是我的错,所以我正在努力理解。

最佳答案

我的回答不是关于你的测试,而是关于单元测试的好处(这是你问题中最重要的部分)。

您看不到编写测试的好处,因为好处会随着时间的推移而增长,日复一日。

例如:拿了一段你十年前写的代码。重构它,使用另一个库,将语言升级到 java 8 并现在使用 lambdas,等等......你想知道它现在是否还在工作吗?通过启动程序并手动测试所有功能?即使该项目有数百万行代码和数千个功能?不,你不能。唯一可以确定的方法是验证在您知道它应该如何工作时编写的先前测试是否仍然通过。

您今天编写的测试证明它像今天预期的那样工作,但最重要的是,它证明每次对实现进行修改时它都能工作。当更改由另一个程序员完成时甚至更多。

现在,回复您的具体案例:在单元测试中,不要专注于测试实现(永远不要那样做),而是专注于行为:当您执行时,程序的结果或状态应该是什么使用特定参数调用方法。 例如,您可以在此处通过使用一些用户填充源来设置测试,并测试方法 getAll() 是否返回预期数据。另一个测试可以验证该方法在没有任何用户时返回空列表(而不是 null)。

关于java - 这是有效的单元测试吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44202672/

相关文章:

java - 亚马逊 AWS RAID 配置

java - 在非默认模块上运行 appengine 端点

mongodb - 如何在 MongoDB 上模拟 'error' 事件

java - junit 4.10 : how to get the name of the tests class while running tests

java - 模拟 SecureRandom::nextInt()

java - assertSame Junit 测试

java - 添加到列表抛出的前面?

java - 有没有办法在不运行 maven build 的情况下测试 war 文件中的代码更改?

unit-testing - Spock 测试框架 : Difference between mock. getProperty ('name' ) vs getName()

python-2.7 - 我想将一个模块从 Python 2 移植到 3,如何运行测试?