Summary: When a class has a @Cacheable method, accessing that class's instance variable(s) in a test via reflection causes NoSuchFieldException.

Environment: Tested with versions 2.7.5 and 3.1.1 of spring-boot-starter-parent.

$ java -version
openjdk version "17.0.4" 2022-07-19 LTS
OpenJDK Runtime Environment Corretto-17.0.4.8.1 (build 17.0.4+8-LTS)
OpenJDK 64-Bit Server VM Corretto-17.0.4.8.1 (build 17.0.4+8-LTS, mixed mode, sharing)
$ mvn -v
Apache Maven 3.6.3
Maven home: /usr/share/maven
Java version: 17.0.4, vendor: Amazon.com Inc., runtime: /home/erik/.sdkman/candidates/java/17.0.4-amzn
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "5.19.0-46-generic", arch: "amd64", family: "unix"

How to recreate: - Generate a Spring Boot project with Spring initializr, with Maven as build tool. - On the main class, add @EnableCaching and @Configuration. - In the same file as that of the main class, add the following code:

@Service
class MyService {
    int x = 1;

    @Cacheable("numbers")
    public List<Integer> somethingSlow() {
        try { Thread.sleep(5000); } catch (InterruptedException e) {}
        return List.of(1,2,3);
    }
}
  • Modify the test file (generated by Spring initializr) to contain this code:
@SpringBootTest
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class CachingNosuchfieldApplicationTests {
    @Autowired MyService myService;

    @BeforeAll
    void setup() throws Exception {
        myService.getClass().getDeclaredField("x");
    }

    @Test void someTest() {
        var l = myService.somethingSlow();

        var start = System.currentTimeMillis();
        l = myService.somethingSlow();
        var stop = System.currentTimeMillis();
        assertTrue(stop - start < 100);
    }
}
  • Run the test with mvn test

Expexted result: Test runs and passes. Actual result: Test is ignored due to NoSuchFieldException: ```java.lang.NoSuchFieldException: x at java.base/java.lang.Class.getDeclaredField


**Observations:**
- If `@Cacheable` is removed, the test runs (and fails due to the assertion failing).
- If the call to `getDeclaredField` is removed, the test runs and passes.
- If `@TestInstance(...)` is removed, `setup` is made static, and `myService.getClass()` is changed to `MyService.class`, the test runs and passes.

**Comment From: snicoll**

I don't understand what you're doing. First of all, I can't reproduce what you've described and that's a general problem with sample in code blocks rather than a link to a GitHub repo that we can't run.

That said, `@Cacheable` creates a CGLIB proxy for your component so `getClass` returns that, rather than the service class. You can use `AopUtils#getTargetClass` to retrieve the underlying user class from a proxy.

**Comment From: erikv85**

Repo created. Recreate instructions:

git clone git@github.com:erikv85/caching-nosuchfield.git mvn -f caching-nosuchfield test ```