单元测试时静态元模型属性为null

时间:2016-03-25 18:05:11

标签: java jpa testng

我在外部包中有@Entity类,它们也有静态元模型。在我的应用程序的服务类中,我使用这些元模型和EntityManager / CriteriaBuilder / CriteriaQuery来检索我的数据。这在运行应用程序时工作正常。但是,在运行单元测试时,我的元模型及其属性始终为null。

...代码

package com.example.core.entities;

@Entity
@Table(schema = "lookup", name="BookingSystem")
public class BookingSystem implements ILookupEntity, IAuditEntity, Serializable {
  @Id
  @GeneratedValue(strategy = IDENTITY)
  @Column(name = "id")
  public Integer id;

  @Column(name = "name")
  public String name;

  @Column(name = "code")
  public Integer code;
}

package com.example.core.entities;

@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(BookingSystem.class)
public abstract class BookingSystem_ {
  public static volatile SingularAttribute<BookingSystem, Integer> id;
  public static volatile SingularAttribute<BookingSystem, Integer> code;
  public static volatile SingularAttribute<BookingSystem, String> name;
}

在我应用的服务类中使用...

package com.example.bookingsystem;

@Service
public class BookingService {
  @PersistenceContext
  private EntityManager entityManager;

public void saveBooking(Booking booking) {
//...
  RepositoryQueryBuilder<BookingSystem> bookingSystemSelector = new RepositoryQueryBuilder<>(entityManager, BookingSystem.class);
  List<BookingSystem> bookingSystems = bookingSystemSelector
      .and(BookingSystem_.code, booking.bookingSystem.code) //<-- Here "BookingSystem_.code" is null.
      .getResultList();
  //...
  }
}

&#34; RepositoryQueryBuilder&#34; class只是一个实用程序构建器类,它包装了EntityManager,CriteriaBuilder等。在这个例子后基本建模... JPA Criteria Predicate Conditions

单元测试代码......

package com.example.bookingsystem;

public abstract class BaseTestSetup {
  @InjectMocks
  protected BookingService bookingService;

  protected EntityManager entityManager = PowerMockito.mock(EntityManager.class);
  protected CriteriaBuilder criteriaBuilder = PowerMockito.mock(CriteriaBuilder.class);
  protected CriteriaQuery<BookingSystem> criteriaQuery = PowerMockito.mock(CriteriaQuery.class);
  protected Root<BookingSystem> root = PowerMockito.mock(Root.class);

  protected void arrange() {
    when(entityManager.getCriteriaBuilder()).thenReturn(criteriaBuilder);
    when(criteriaBuilder.createQuery(BookingSystem.class)).thenReturn(criteriaQuery);
    when(criteriaQuery.from(Matchers.<Class<BookingSystem>>any())).thenReturn(root);
    when(criteriaQuery.from(Matchers.<EntityType<BookingSystem>>any())).thenReturn(root);
}

}

@RunWith(PowerMockRunner.class)
public class BookingServiceTest extends BaseTestSetup {
  @BeforeClass
  @Override
  public void arrange() {
    super.arrange();

    //...
}

@Test
public void doIt() {
    Booking booking = new Booking();
    booking.id = 12345;
    booking.bookingSystem = new BookingSystem();
    booking.bookingSystem.id = 1;
    booking.bookingSystem.code = 106000;

    bookingService.saveBooking(booking);
  }
}

我已经查看了这个JPA/Hibernate Static Metamodel Attributes not Populated -- NullPointerException,但解决方案似乎是&#34;确保实体及其元模型位于同一个软件包中,但正如您所看到的,两者都已经在我的&#34; com.example.core.entities&#34;封装

我在我的代码中使用了所有bean和注释驱动的配置(没有持久性或上下文xml文件)。就测试而言,我在IntelliJ中使用了TestNG和PowerMock。

似乎在单元测试期间没有拾取元模型。任何想法。

4 个答案:

答案 0 :(得分:3)

加载hibernate时会填充静态元模型类。因此,要么在测试中配置hibernate上下文,要么在方法执行之前手动填充属性。在你的代码中,你可以这样做:

@Test
public void doIt() {
    BookingSystem_.code = new SingularAttributeMock<BookingSystem, Integer>();
    bookingService.saveBooking(booking);
  }
}

可以创建类 SingularAttributeMock ,以便在测试中使用它。您还可以使用 SingularAttribute 类的任何其他实现。

public class SingularAttributeMock<X, Y> implements SingularAttribute<X, Y> {
   //Overriding methods of SingularAttribute...
}

答案 1 :(得分:0)

无需手动初始化。 您应该遵守以下规则:

  • 元模型类应声明为抽象类。
  • 元模型类应与它们的实体类位于同一个包中 描述;
  • 它们应与实体类具有相同的名称 描述,然后是下划线(例如,产品是实体, Product_是元模型类);
  • 如果实体继承自另一个实体 实体或来自映射的超类,其元模型类应该 继承自描述其立即数的元模型类 超类(例如,如果SpecialProduct扩展Product,则扩展 PersistentObject,然后SpecialProduct_应该扩展Product_ 应该扩展PersistentObject _)。

答案 2 :(得分:0)

我建议不要让自己的班级创建Mockito来为您完成这项工作。

var printNumTwo;
for (var i = 0; i < 3; i++) {
  if (i === 2) {
    printNumTwo = function() {
      return i;
    };
  }
}
console.log(printNumTwo());
// returns 3 

但是值得一提的是,将元模型引入服务层并不是很好的做法,您宁愿将其推到DAO方法中。

答案 3 :(得分:0)

在我的情况下,无法模拟元模型,因此我只能从entityManager中获取它。

private EntityManager entityManager;

@Before
public void init() {
    // Assume that entityManager was correctly initialized.

    this.configMetaModel();
}
private void configMetaModel() {

   Metamodel metamodel = this.entityManager.getMetamodel();

   BiFunction<EntityType<MY_ENTITY>, String, Attribute>
                bfAttr = (entity, field) -> entity.getAttributes()
                .stream()
                .filter(a -> a.getName().equals(field))
                .findAny()
                .get();

   Function<Attribute, SingularAttribute<MY_ENTITY, String>> fToStr = attribute ->
            (SingularAttribute<MY_ENTITY, String>) attribute;

   Function<Attribute, SingularAttribute<MY_ENTITY, LocalDate>> fToDate = attribute ->
            (SingularAttribute<MY_ENTITY, LocalDate>) attribute;

   EntityType<MY_ENTITY> entity = metamodel.entity(MY_ENTITY.class);
   MY_ENTITY_.id = fToStr.apply(bfAttr.apply(entity, "id"));
   MY_ENTITY_.name = fToStr.apply(bfAttr.apply(entity, "name"));
   MY_ENTITY_.someDate = fToDate.apply(bfAttr.apply(entity, "someDate"));
}

“ MY_ENTITY” 替换我的实体

“ MY_ENTITY _” 替换我的实体元模型

完成此操作后,就可以完美运行所有单元测试。