Java 8 Clock으로 클래스 단위 테스트
java.time.Clock
다른 많은 java.time
객체에 대한 인수로 사용할 수있는 Java 8이 도입 되어 실제 또는 가짜 시계를 삽입 할 수 있습니다. 예를 들어,을 (를) Clock.fixed()
만든 다음 호출 할 수 있다는 것을 알고 있으며 제공 Instant.now(clock)
한 고정 값 Instant
을 반환합니다 . 이것은 단위 테스트에 완벽하게 들립니다!
그러나 이것을 사용하는 가장 좋은 방법을 찾는 데 어려움이 있습니다. 다음과 유사한 수업이 있습니다.
public class MyClass {
private Clock clock = Clock.systemUTC();
public void method1() {
Instant now = Instant.now(clock);
// Do something with 'now'
}
}
이제이 코드를 단위 테스트하고 싶습니다. 다른 시간에 clock
테스트 할 수 있도록 고정 된 시간을 생성 하도록 설정할 수 있어야합니다 method()
. 분명히 리플렉션을 사용하여 clock
멤버를 특정 값 으로 설정할 수 있지만 리플렉션 에 의존 할 필요가 없으면 좋을 것입니다. 공개 setClock()
방법을 만들 수는 있지만 잘못된 것 같습니다. Clock
실제 코드가 시계를 전달하는 것과 관련이 없어야하기 때문에 메서드에 인수 를 추가하고 싶지 않습니다.
이를 처리하는 가장 좋은 방법은 무엇입니까? 이것은 새로운 코드이므로 수업을 재구성 할 수 있습니다.
편집 : 명확히하기 위해 단일 MyClass
개체 를 구성 할 수 있어야 하지만 한 개체가 두 개의 다른 시계 값을 볼 수 있어야합니다 (일반적인 시스템 시계가 똑딱 거리는 것처럼). 따라서 고정 시계를 생성자에 전달할 수 없습니다.
Jon Skeet의 답변과 주석을 코드에 입력하겠습니다.
테스트중인 클래스 :
public class Foo {
private final Clock clock;
public Foo(Clock clock) {
this.clock = clock;
}
public void someMethod() {
Instant now = clock.instant(); // this is changed to make test easier
System.out.println(now); // Do something with 'now'
}
}
단위 테스트 :
public class FooTest() {
private Foo foo;
private Clock mock;
@Before
public void setUp() {
mock = mock(Clock.class);
foo = new Foo(mock);
}
@Test
public void ensureDifferentValuesWhenMockIsCalled() {
Instant first = Instant.now(); // e.g. 12:00:00
Instant second = first.plusSeconds(1); // 12:00:01
Instant thirdAndAfter = second.plusSeconds(1); // 12:00:02
when(mock.instant()).thenReturn(first, second, thirdAndAfter);
foo.someMethod(); // string of first
foo.someMethod(); // string of second
foo.someMethod(); // string of thirdAndAfter
foo.someMethod(); // string of thirdAndAfter
}
}
실제 코드는 시계 전달과 관련이 없어야하기 때문에 메서드에 Clock 인수를 추가하고 싶지 않습니다.
아니요 ...하지만 생성자 매개 변수 로 간주 할 수 있습니다 . 기본적으로 수업에 작동 할 시계가 필요하다고 말하는 것입니다. 따라서 그것은 종속성입니다. 다른 종속성과 마찬가지로 처리하고 생성자 또는 메서드를 통해 주입하십시오. (개인적으로 생성자 주입을 선호하지만 YMMV.)
As soon as you stop thinking of it as something you can easily construct yourself, and start thinking of it as "just another dependency" then you can use familiar techniques. (I'm assuming you're comfortable with dependency injection in general, admittedly.)
I'm a bit late to the game here, but to add to the other answers suggesting using a Clock - this definitely works, and by using Mockito's doAnswer you can create a Clock which you can dynamically adjust as your tests progress.
Assume this class, which has been modified to take a Clock in the constructor, and reference the clock on Instant.now(clock)
calls.
public class TimePrinter() {
private final Clock clock; // init in constructor
// ...
public void printTheTime() {
System.out.println(Instant.now(clock));
}
}
Then, in your test setup:
private Instant currentTime;
private TimePrinter timePrinter;
public void setup() {
currentTime = Instant.EPOCH; // or Instant.now() or whatever
// create a mock clock which returns currentTime
final Clock clock = mock(Clock.class);
when(clock.instant()).doAnswer((invocation) -> currentTime);
timePrinter = new TimePrinter(clock);
}
Later in your test:
@Test
public void myTest() {
myObjectUnderTest.printTheTime(); // 1970-01-01T00:00:00Z
// go forward in time a year
currentTime = currentTime.plus(1, ChronoUnit.YEARS);
myObjectUnderTest.printTheTime(); // 1971-01-01T00:00:00Z
}
You're telling Mockito to always run a function which returns the current value of currentTime whenever instant() is called. Instant.now(clock)
will call clock.instant()
. Now you can fast-forward, rewind, and generally time travel better than a DeLorean.
To start, definitely inject a Clock
into your class under test, as recommended by @Jon Skeet. If your class only requires one time, then simply pass in a Clock.fixed(...)
value. However, if your class behaves differently across time e.g. it does something at time A, and then does something else at time B, then note that the clocks created by Java are immutable, and thus cannot be changed by the test to return time A at one time, and then time B at another.
Mocking, as per the accepted answer, is one option, but does tightly couple the test to the implementation. For example, as one commenter points out, what if the class under test calls LocalDateTime.now(clock)
or clock.millis()
instead of clock.instant()
?
An alternate approach that is a bit more explicit, easier to understand, and may be more robust than a mock, is to create a real implementation of Clock
that is mutable, so that the test can inject it and modify it as necessary. This is not difficult to implement. See https://github.com/robfletcher/test-clock for a good example of this.
MutableClock c = new MutableClock(Instant.EPOCH, ZoneId.systemDefault());
ClassUnderTest classUnderTest = new ClassUnderTest(c);
classUnderTest.doSomething()
assertTrue(...)
c.instant(Instant.EPOCH.plusSeconds(60))
classUnderTest.doSomething()
assertTrue(...)
You either give MyClass
a clock, mock Clock
, orretrieve a clock–there aren't many more options.
You don't need to do the reflection yourself, you use a mocking library.
Opinions vary on what the "correct" approach is.
ReferenceURL : https://stackoverflow.com/questions/27067049/unit-testing-a-class-with-a-java-8-clock
'IT Share you' 카테고리의 다른 글
파이썬의 numpy에서“zip ()”에 해당하는 것은 무엇입니까? (0) | 2020.12.15 |
---|---|
JS 코드가 "var a = document.querySelector ( 'a [data-a = 1]');"인 이유 (0) | 2020.12.15 |
Node / Express로 엔터프라이즈 앱 빌드 (0) | 2020.12.15 |
PhpMailer 대 SwiftMailer? (0) | 2020.12.15 |
PHP에서 슈퍼 글로벌 변수를 만드시겠습니까? (0) | 2020.12.15 |