2014-10-16 93 views
2

我有以下的Grails 2.4.3域类GORM公式没有计算

package invoicer 

class Product { 
    Float price 
    Float taxRate 
    Float tax 

    static mapping = { 
     tax formula: 'PRICE * TAX_RATE' 
    } 
} 

我创造了这个整合测试,以验证公式被正确计算

package invoicer; 

import spock.lang.Specification 

class ProductIntegrationSpec extends Specification { 

    def "Test tax calculation"() { 
     when: 
     def p = new Product(price: 5.00, taxRate: 0.25) 
     p.save(failOnError:true, flush: true) 

     then: 
     def newProduct = Product.get(1) 
     newProduct.tax == (5.00 * 0.25) 
    } 
} 

此测试总是失败因此

grails> test-app -integration ProductIntegrationSpec 
2014-10-16 11:29:15,225 [main] DEBUG hibernate.SQL - 
    drop table product if exists 
2014-10-16 11:29:15,230 [main] DEBUG hibernate.SQL - 
    create table product (
     id bigint generated by default as identity, 
     version bigint not null, 
     price float not null, 
     tax_rate float not null, 
     primary key (id) 
    ) 
====================================================================== 
         Application: Invoicer 0.1      
         -------------------------      
    Environment: TEST 
    Database configuration: 
    Hibernate DDL mode: create 
    URL: jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE 
    Driver: org.h2.Driver 
    User: sa 
====================================================================== 
| Running 1 integration test... 1 of 1 
2014-10-16 11:29:17,797 [main] DEBUG hibernate.SQL - 
    /* insert invoicer.Product 
     */ insert 
     into 
      product 
      (id, version, price, tax_rate) 
     values 
      (null, ?, ?, ?) 
2014-10-16 11:29:17,798 [main] TRACE sql.BasicBinder - binding parameter [1] as [BIGINT] - [0] 
2014-10-16 11:29:17,798 [main] TRACE sql.BasicBinder - binding parameter [2] as [FLOAT] - [5.0] 
2014-10-16 11:29:17,798 [main] TRACE sql.BasicBinder - binding parameter [3] as [FLOAT] - [0.25] 
| Failure: Test tax calculation(invoicer.ProductIntegrationSpec) 
| Condition not satisfied: 
newProduct.tax == (5.00 * 0.25) 
|   | |  | 
|   | false 1.2500 
|   null 
invoicer.Product : 1 
    at invoicer.ProductIntegrationSpec.Test tax calculation(ProductIntegrationSpec.groovy:14) 
| Completed 1 integration test, 1 failed in 0m 0s 
| Tests FAILED - view reports in /Users/XXX/git/invoicer/target/test-reports 

我不明白为什么这是失败的,所以我尝试了在Grails控制台w第i个验证码

import invoicer.* 

def p = new Product(price: 5.00, taxRate: 0.25) 
p.save(failOnError: true, flush: true) 

def newProduct = Product.get(1) 
assert newProduct.tax == (5.00 * 0.25) 

它未能在第一时间在控制台

groovy>  import invoicer.* 
groovy>  
groovy>  def p = new Product(price: 5.00, taxRate: 0.25) 
groovy>  p.save(failOnError: true, flush: true) 
groovy>  
groovy>  def newProduct = Product.get(1) 
groovy>  assert newProduct.tax == (5.00 * 0.25) 

2014-10-16 11:34:31,140 [Thread-12] DEBUG hibernate.SQL - 
    /* insert invoicer.Product 
     */ insert 
     into 
      product 
      (id, version, price, tax_rate) 
     values 
      (null, ?, ?, ?) 

2014-10-16 11:34:31,162 [Thread-12] TRACE sql.BasicBinder - binding parameter [1] as [BIGINT] - [0] 

2014-10-16 11:34:31,163 [Thread-12] TRACE sql.BasicBinder - binding parameter [2] as [FLOAT] - [5.0] 

2014-10-16 11:34:31,164 [Thread-12] TRACE sql.BasicBinder - binding parameter [3] as [FLOAT] - [0.25] 

Exception thrown 

Assertion failed: 

assert newProduct.tax == (5.00 * 0.25) 
     |   | |  | 
     |   | false 1.2500 
     |   null 
     invoicer.Product : 1 

    at ConsoleScript0.run(ConsoleScript0:7) 
    at org.springsource.loaded.ri.ReflectiveInterceptor.jlrMethodInvoke(ReflectiveInterceptor.java:1270) 

当同一控制台会话虽然再次运行来看,它工作得很好,以同样的方式

groovy>  import invoicer.* 
groovy>  
groovy>  def p = new Product(price: 5.00, taxRate: 0.25) 
groovy>  p.save(failOnError: true, flush: true) 
groovy>  
groovy>  def newProduct = Product.get(1) 
groovy>  assert newProduct.tax == (5.00 * 0.25) 

2014-10-16 11:36:20,612 [Thread-13] DEBUG hibernate.SQL - 
    /* insert invoicer.Product 
     */ insert 
     into 
      product 
      (id, version, price, tax_rate) 
     values 
      (null, ?, ?, ?) 

2014-10-16 11:36:20,613 [Thread-13] TRACE sql.BasicBinder - binding parameter [1] as [BIGINT] - [0] 

2014-10-16 11:36:20,613 [Thread-13] TRACE sql.BasicBinder - binding parameter [2] as [FLOAT] - [5.0] 

2014-10-16 11:36:20,614 [Thread-13] TRACE sql.BasicBinder - binding parameter [3] as [FLOAT] - [0.25] 

2014-10-16 11:36:20,620 [Thread-13] DEBUG hibernate.SQL - 
    select 
     product0_.id as id1_7_0_, 
     product0_.version as version2_7_0_, 
     product0_.price as price3_7_0_, 
     product0_.tax_rate as tax_rate4_7_0_, 
     product0_.PRICE * product0_.TAX_RATE as formula0_0_ 
    from 
     product product0_ 
    where 
     product0_.id=? 

2014-10-16 11:36:20,621 [Thread-13] TRACE sql.BasicBinder - binding parameter [1] as [BIGINT] - [1] 

请注意,有一个select语句记录了第二次运行的计算税,但没有第一次运行!我很难理解第一次为什么控制台行为失败,为什么它第二次正常运行。我想这个问题的答案会解释为什么集成测试总是失败。谢谢你的想法!

+0

只是一些无关的建议:永远不要使用浮动货币计算。早于后来,你会追逐便士错误或更糟。使用'BugDecimal'。 – cfrick 2014-10-16 21:01:26

+0

感谢您的回复@cfrick。是的,Float是不好的,但是这个问题让我陷入了困境很长一段时间,我退化到使用GORM文档中的例子。 – 2014-10-17 01:36:31

回答

3

我终于明白了。调用

save(flush: true) 

是不够的。关键是重新查询数据库,因为那是where子句生成公式的时候。保存域对象后,必须调用

refresh() 

就可以了。这将重新查询数据库并填充基于公式的字段。

+0

注意:这不适用于集成测试。 '@ Rollback'注释使得每个功能测试都在一个事务中运行,[显然](https://stackoverflow.com/a/4686944/2521769),'flush:true'和'refresh()'没有直到交易结束后才有效果。 – 2017-10-20 08:54:23