【问题标题】:Spock believes inner enum of tested class should be a property of SpecSpock 认为测试类的内部枚举应该是 Spec 的属性
【发布时间】:2025-12-15 02:40:01
【问题描述】:

我有一个尝试用 Spock 测试的 Java 类。 Java 类包含一个内部枚举:

import static java.util.Calendar.*;
import java.util.*;

public class FederalHolidays {

    public enum Observance {
        NEW_YEARS_DAY(JANUARY, 1),
        BIRTHDAY_OF_MARTIN_LUTHER_KING_JR(JANUARY, MONDAY, 3),
        WASHINGTONS_BIRTHDAY(FEBRUARY, MONDAY, 3),
        MEMORIAL_DAY(MAY, MONDAY, -1),
        INDEPENDENCE_DAY(JULY, 4),
        LABOR_DAY(SEPTEMBER, MONDAY, 1),
        COLUMBUS_DAY(OCTOBER, MONDAY, 2),
        VETERANS_DAY(NOVEMBER, 11),
        THANKSGIVING_DAY(NOVEMBER, THURSDAY, 4),
        CHIRSTMAS_DAY(DECEMBER, 25);

        private final int month;
        private final int dayOfMonth;
        private final int dayOfWeek;
        private final int weekOfMonth;
        private static final int NA = 0;

        private Observance(int month, int dayOfMonth) {
            this.month = month;
            this.dayOfMonth = dayOfMonth;
            this.dayOfWeek = NA;
            this.weekOfMonth = NA;
        }

        private Observance(int month, int dayOfWeek, int weekOfMonth) {
            this.month = month;
            this.dayOfMonth = NA;
            this.dayOfWeek = dayOfWeek;
            this.weekOfMonth = weekOfMonth;
        }

        boolean isFixedDate() {
            return dayOfMonth != NA;
        }
    }

    public Date dateOf(Observance observance, int year) {
        Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("EST"), Locale.ENGLISH);
        cal.set(YEAR, year);
        cal.set(MONTH, observance.month);
        cal.clear(HOUR);
        if (observance.isFixedDate()) {
            cal.set(DAY_OF_MONTH, observance.dayOfMonth);
        } else {
            setNthDayOfWeek(cal, observance.dayOfWeek, observance.weekOfMonth);
        }
        adjustForWeekendsIfNecessary(cal);
        return cal.getTime();
    }

    private void setNthDayOfWeek(Calendar cal, int dayOfWeek, int n) {
        int week = 0;
        int lastDay = cal.getActualMaximum(DAY_OF_MONTH);
        int startDay = n > 0 ? 1 : lastDay;
        int endDay = n > 0 ? lastDay : 1;
        int incrementValue = n > 0 ? 1 : -1;
        for (int day = startDay; day != endDay; day += incrementValue) {
            cal.set(DAY_OF_MONTH, day);
            if (cal.get(DAY_OF_WEEK) == dayOfWeek) {
                week += incrementValue;
                if (week == n) {
                    return;
                }
            }
        }
    }

    private void adjustForWeekendsIfNecessary(Calendar cal) {
        int dayOfWeek = cal.get(DAY_OF_WEEK);
        cal.add(DAY_OF_MONTH, dayOfWeek == SATURDAY ? -1 : dayOfWeek == SUNDAY ? 1 : 0);
    }
}

我的 Spock 测试规范如下所示:

 class FederalHolidaysSpec extends Specification {

    @Shared
    def federalHolidays = new FederalHolidays()

    def "holidays are correctly calculated"() {
        expect:
            federalHolidays.dateOf(observance, year).format('yyyy/MM/dd') == date
        where:
            observance                        | year | date
            NEW_YEARS_DAY                     | 2011 | '2010/12/31'
            BIRTHDAY_OF_MARTIN_LUTHER_KING_JR | 2011 | '2011/01/17'
            WASHINGTONS_BIRTHDAY              | 2011 | '2011/02/21'
            MEMORIAL_DAY                      | 2011 | '2011/05/30'
            INDEPENDENCE_DAY                  | 2011 | '2011/07/04'
            LABOR_DAY                         | 2011 | '2011/09/05'
            COLUMBUS_DAY                      | 2011 | '2011/10/10'
            VETERANS_DAY                      | 2011 | '2011/11/11'
            THANKSGIVING_DAY                  | 2011 | '2011/11/24'
            CHIRSTMAS_DAY                     | 2011 | '2011/12/26'
    }
}

当我运行 Spock 时,我得到了 10 个测试错误,表格的每一行都有一个。每个错误都是相同的:

groovy.lang.MissingPropertyException: No such property: Observance for class:   bdkosher.FederalHolidaysSpec
    at bdkosher.FederalHolidaysSpec.$spock_initializeFields(FederalHolidaysSpec.groovy)

(我正在使用 Spock 0.7-groovy-2.0、groovy-all 2.2.2(非 indy)和 Java 1.7.0_45。)

Spock 为什么要在我的 FederalHolidaysSpec 类中寻找名为 Observance 的属性? 我最初怀疑这个问题与内部枚举/静态导入有关,尽管将我的 Spock 测试更改为使用完全限定的枚举值(例如 bdkosher.FederalHolidays.Observance.CHRISTMAS_DAY)并没有产生任何影响。

编辑:Java 文件实现已更正,因此它通过了测试;测试文件中的错别字已更正。

【问题讨论】:

  • enum 不是也必须公开吗?
  • 是的,很好(我还没有尝试在它的包之外使用 API)。但即使我在上面加上public 修饰符,我仍然会得到相同的测试错误。
  • @BDKosher 我检查了你的代码。这个对我有用。我的设置:groovy 2.2.1,spock 0.7-groovy-2.0。除了多个拼写错误和 2 个错误计算的日期外,一切运行顺利。

标签: java groovy enums spock


【解决方案1】:

确保源代码和测试用例都有正确的包。如果 java 类和测试类都在同一个包中,下面对我有用。

package bdkosher

import spock.lang.Shared
import spock.lang.Specification
//import bdkosher.FederalHolidays.Observance //Do not need
import static bdkosher.FederalHolidays.Observance.*

class FederalHolidaysSpec extends Specification { .. }

我在没有默认包名称 com.example 的情况下进行了测试,并且在 Grails 应用程序中进行了测试,尽管 spock 版本相同。

【讨论】:

    【解决方案2】:

    在听说 @elems' 和 @dmahaptro 的成功之后,我开始从我的项目结构中剔除一些东西,并减少我 POM 中的依赖关系。

    在完全删除未使用的 src/test/javasrc/main/groovy 目录后,我注意到我的 IDE 中的 FederalHolidaysSpec.groovy 源文件恢复为旧版本(我不确定是否应该为此责备 gmavenplus-plugin 或 NetBeans --我怀疑是后者,因为我一直在使用mvn clean test 进行测试)。无论如何,这个旧版本包含一个额外的字段:

    class FederalHolidaysSpec extends Specification {
    
        def data = [(Observance.NEW_YEARS_DAY) : ['2011/12/31']]
    

    该字段导致了测试错误。即使使用import bdkosher.FederalHolidays.Observance,Groovy 显然认为映射键是属性引用而不是enum 引用?

    无论如何,现在我可以修复拼写错误和合法的测试失败。感谢您的帮助。

    【讨论】:

    • 我不完全确定 Groovy 是否支持导入嵌套类。
    • 看起来是这样的。使用 FQN--def data = [(gov.uspto.trademark.tmng.datetime.FederalHolidays.Observance.NEW_YEARS_DAY) : ['2011/12/31']]--不会导致任何错误。至少,静态导入按我的意图工作。
    • import gov.uspto.trademark.tmng.datetime.FederalHolidays(FederalHolidays.Observance.NEW_YEARS_DAY) 可能也可以。
    • 是的,这行得通。如果我import gov.uspto.trademark.tmng.datetime.FederalHolidays.Observance 并定义一个字段def newYears = Observance.NEW_YEARS_DAY,Groovy 没有任何抱怨。将其用作 Map 键时一定会出现问题。