【问题标题】:Returning object from create method in DAO从 DAO 中的 create 方法返回对象
【发布时间】:2013-02-21 21:30:31
【问题描述】:

我有以下情况。我想将设置创建日期的责任委托给 DAO 对象。

    Class Product{
     //other fields
     Date creationDate;
    }

    Class ProductDAO{
      private GenericDAO dao;
      public void create(Product p){
         p.setCreationDate(new Date());
         dao.create(p);
      }
    }

    Class Main{
      private ProductDAO productDAO; 
      public void createProduct(){
         Product p = new Product();
         productDAO.create(p);
         LOG.debug(p.getCreationDate());
      } 
    }
  1. 现在的问题是,当我通过以下方式测试主类的 createProduct 方法时 模拟productDAO,如何定义模拟对象的行为 'productDAO' 以便它设置创建日期和日志语句 不会抛出 NullPointerException?
  2. 上述方法看起来正确吗?我应该退回更新的产品吗 来自 ProductDAO 类的 create 方法的对象,而不是依赖于 副作用?

     Product create(Product p){
           p.setCreationDate(new Date());
           dao.create(p);
           return p;
      }
    
  3. 或者在主类中,我应该在之后从数据库中读取对象吗 创建而不是依赖于返回对象或副作用 创建产品方法?在这种情况下,唯一的事情是它会增加额外调用的开销。

     public void createProduct(){
         Product p = new Product();
         productDAO.create(p);
         p = productDAO.read(p.getId());
         LOG.debug(p.getCreationDate());
      } 
    

抱歉,问题太长了。希望我能解释一下。

【问题讨论】:

  • 什么是GenericDAO
  • 你在使用 JPA 吗?
  • 为什么不只是模拟 GenericDAO 而不是 ProductDAO?
  • GenericDAO 是具有泛型类型参数的泛型 dao 类。 GenericDAO<Product> dao.
  • @EricGalluzzo,我正在测试 Main 类,所以我不应该只模拟直接依赖项吗?

标签: java


【解决方案1】:

测试日期是否设置的一种方法是使用argThat。你需要做类似的事情

@Test
public void shouldSetCreationDateAtProductWhenCreatingIt() {
     // given
     Product product = mock(Product.class);

     // when
     dao.create(product);

     // then
     verify(product).setCreationDate(argThat(is(not(nullValue(Date.class)))));
}

所以,我只做了一个简单的验证,即Product#setCreationDate(Date) 被调用了非空值Date。您可以创建自定义匹配器以查看 Date 对象是否是您期望的对象。这只是为了测试类ProductDao

如果您想以需要模拟 getCreationDate 返回的方式对 Main 进行单元测试,您可以向您的 ProductDao#create 添加返回,使其返回持久对象。

 public class ProductDao {
     public Product create(Product p){
        p.setCreationDate(new Date());
        dao.create(p);
        return p;
     }
 }

然后你可以在你的 Main 类中模拟 productDao 的返回

  @Test
  public void shouldUseReturnedProductFromDaoToLog() {
       // given
       Product product = mock(Product.class);
       Date creationDate = new Date();

       given(product.getCreationDate()).willReturn(creationDate);
       given(dao.create(any(Product.class)).willReturn(product);

       // when      
       Product createdProduct = main.createProduct();

       // then
       assertThat(createdProduct, is(equalTo(product)));
  }

我向 createProduct 添加了一个返回,因为我想测试它是否与从 DAO 返回的相同。然后,我们将查看ProductDao 是否符合预期(设置creationDate)与Main(创建产品并在Dao 中持久化)的单元测试分开

希望这会有所帮助。

【讨论】:

    【解决方案2】:
    1. 我认为一个完全有效的方法是创建一个 Product 实例,它有一个 creationDate “set”,并让你的 mockProductDao 返回它。

      Date timeStamp = new Date();
      Product productWithCreationDate = new Product();
      productWithCreationDate.setCreationDate(timeStamp);
      when(mockProductDao.create(anyObject()).thenReturn(productWithCreationDate);
      
    2. 我认为您不需要从您的 ProductDAO 返回 p,因为您在任何地方调用 productDAO.create(p),那么您可以在下一行访问 p(带有 creationDate) -对吗?

    3. 如果您不信任 DAO 层,您只需要从 DAO 中读取 p :) 因此,如果您确信如果 create() 方法失败,DAO 层将抛出正确的异常,我认为你不需要重新阅读p

    【讨论】:

    • 如果另一个程序员忘记设置创建日期怎么办。我想确保始终设置创建日期。我正在使用 DynamoDB,所以我不能使用 Not null 约束或类似的东西。
    • 假设另一个程序员也应该使用你的同一个 DAO,那么这很容易——你只需要测试你的 DAO 层。
    【解决方案3】:

    为什么需要通过 setter 分配创建 Date?只需在构造函数中分配 Date

    Product p = new Product();  //here you set creation date
    productDAO.create(p);  
    

    【讨论】:

    • 我正在为 DynamoDB 使用 ORM,它需要空的构造函数,这就是为什么还需要设置器的原因,这创造了另一个程序员也可以使用它的可能性。
    • 空构造函数是什么意思?默认构造函数里面可以有代码,但不能有参数,只需要在默认构造函数中创建日期
    猜你喜欢
    • 1970-01-01
    • 2020-12-26
    • 1970-01-01
    • 2016-10-13
    • 1970-01-01
    • 1970-01-01
    • 2016-03-18
    • 2021-01-30
    • 1970-01-01
    相关资源
    最近更新 更多