这是一个有效的单元测试吗?

时间:2017-05-26 13:18:46

标签: java unit-testing junit mockito

(我知道人的复数是人,我更喜欢人......)

我试图了解测试的本质。我有一个玩具项目,在这个玩具项目中,我有以下课程:

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

当我运行项目时,此方法可以正常工作。我向EntityManagerService注入getAll(),当我在PersonDaoImpl中调用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

这是一个有效的单元测试吗?如果没有,是因为我在var app = angular.module('myApp', []); app.controller('myCtrl', function($scope) { $scope.names = [ {name:'Priya',age:'19',gender:'Female',English:x[0], Hindi:x[1]}, ... ... {name:'Jiah', age:'18', gender:'Female',English:x[18],Hindi:x[19]} ]; $scope.sortColumn ="name"; $scope.$watch('names', function(newVal) { var total = 0; angular.forEach(newVal, function(x) { total += parseInt(x.English) + parseInt(x.Hindi); }); $scope.total = total }, true); $scope.delete = function (name) { $scope.names.splice( $scope.names.indexOf(name), 1 ); } }); 类中设计不好,我该如何正确地测试这个方法呢?

如果是的话,你能否澄清一下它的真正测试内容以及它如何帮助我重构或让我确信我做的正确/我正在做我正在做的事情?

因为目前,我认为写这个测试绝对没有好处,但我很确定这是我的错,所以我想了解。

2 个答案:

答案 0 :(得分:2)

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

您无法看到写测试的好处,因为这种好处会随着时间的推移而日益增加。

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

您今天编写的测试证明它的工作方式与今天的预期相同,但最重要的是,它是每次对实现进行修改时都能运行的证据。当更改由另一位程序员完成时更是如此。

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

答案 1 :(得分:1)

从技术上讲,这是一个有效的单元测试。在实践中,它没用。

如果您遵循TDD,则首先编写集成测试。通过这些测试运行和传递,您会注意到该单元的所有代码都已得到充分覆盖。所以你不会写那个无用的测试。

如果有更多的内部逻辑,集成测试可能无法有效且充分地运用它,并且单元测试会很有用。

首先编写单元测试的默认可能是对如何测试最常见的误解。更具体地说,系统/集成/单元的区别在TDD中并不存在,而是有接受和开发人员测试,其中:

  • 客户可以看到验收测试

  • 开发人员测试可以帮助开发人员创建通过验收测试的软件

在那些开发人员测试中:

  • 某些开发人员测试适用于自然隔离的单元;一个没有依赖。

  • 有些人使用人工隔离的;使用模拟来替换依赖项。

  • 有些人使用一个单元及其依赖关系,回复先前的测试并接受(通常是微不足道的)减速和发现潜在故障的复杂性。

但你很少会做的是加倍开发人员测试,两次测试同一个单元,一次隔离,一次依赖。这样做不太可能有助于实现验收测试的目标。