for example:I want to change LocalDateTime.now() return result in non-production enviroment, I am trying to invoke the following code,but error occors:
@Test
public void test3() {
System.out.println(LocalDateTime.now());
Instrumentation instrumentation = ByteBuddyAgent.install();
Class<?>[] cls = instrumentation.getAllLoadedClasses();
for (Class<?> cl : cls) {
if ("java.time.LocalDateTime".equals(cl.getName())) {
try {
LocalDateTime mockData = LocalDateTime.of(2020, 1, 1, 0, 0, 0, 0);
byte[] bytes = new ByteBuddy()
.redefine(LocalDateTime.class)
.method(ElementMatchers.named("now").and(ElementMatchers.takesNoArguments()))
.intercept(FixedValue.value(mockData))
.make()
.getBytes();
ClassDefinition classDefinition = new ClassDefinition(cl, bytes);
instrumentation.redefineClasses(classDefinition);
} catch (Throwable e) {
e.printStackTrace();
}
}
}
System.out.println(LocalDateTime.now());
}
2024-03-03T15:33:59.291699 java.lang.UnsupportedOperationException: class redefinition failed: attempted to change the schema (add/remove fields) at java.instrument/sun.instrument.InstrumentationImpl.redefineClasses0(Native Method) at java.instrument/sun.instrument.InstrumentationImpl.redefineClasses(InstrumentationImpl.java:195) at com.yi.component.test.agent.bytecode.ByteBuddyTest.test3(ByteBuddyTest.java:76) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306) at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63) at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329) at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293) at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306) at org.junit.runners.ParentRunner.run(ParentRunner.java:413) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69) at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38) at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11) at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35) at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232) at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
I want to change LocalDateTime.now() return result in non-production enviroment
It sounds like what you actually want to do is be able to control the time in your tests, which is very sensible.
The simpler way to go about this is using dependency injection: instead of having the class you want to test know about the system clock, pass in the clock so that you can vary the clock you use for testing and for production.
As an example, let's say you need to compute fines for loaned items that are overdue (e.g. for a library or something). Your business rules may be:
Note that I'm using recent tools: JUnit 5 and records were introduced in Java 14 (I'm on 21).
Here is a definition of a loaned item:
and test cases expressing the behaviour:
The key thing to note is that
FineCalculatortakes, as a constructor parameter, an instance ofjava.time.Clock.Clock.fixedcreates aClockwhose time is fixed at what you give it. In the first test, then, I create a clock that's fixed at two days after the item's due date:so we expect a fine of 2.
The second case is similar: I pass a clock that's fixed at 7 days before the due date, so we expect a fine of 0.
The implementation of
FineCalculatorthen uses the clock to work out what today is and calculate the fine:When you create the
FineCalculatorin the production code, you'd pass the system clock. Here's a samplemain: