DateTimeImmutable: diff를 반환하는 함수 테스트에서 new DateInterval의 경우 복사
DateTimeImmutable: diff의 함수를 되돌려주는 테스트에서 new DateInterval이면 assertEqual이 복사되어 곤란합니다
날짜 간의 차이를 계산할 때
DateTimeImmutable::diff()
(또는DateTime::diff()
를 사용하려고 합니다.PHP: DateTime::diff - Manual
이 함수의 반환값은
DateInterval
형식으로 시간의 '간격' 을 나타내는 클래스입니다.이 클래스 자체는 매우 편리하지만 DateInterval
형은 특유의 행위가 있어 반환 함수를 테스트하려면 문제가 발생할 수 있다.예: 테스트할 함수
'마감' 을 가진 대상이 존재하고, 함수가 남은 시간을 되돌려줍니다.디자인이 상당히 적합하다.
<?php
final class ItemWithDeadline
{
/** @var DateTimeImmutable */
private $deadline;
......
/**
* @param DateTimeImmutable $now
* @return DateInterval
*/
public function getRemainingInterval(DateTimeImmutable $now)
{
return $now->diff($this->deadline);
}
}
이에 대응하는 PHPUnit 테스트final class GetRemainingIntervalTest extends TestCase
{
public function test_未来の締切に対して正しい残り時間が返る ()
{
$now = DateTimeImmutable::createFromFormat('Y-m-d h:i:s', '2017-12-04 00:00:00');
$item = new ItemWithDeadline([
'deadline' => new DateTimeImmutable('2017-12-05 00:00:00'),
......
]);
$this->assertEquals(
DateInterval::createFromDateString('1 day'),
$item->getRemainingInterval($now)
); // fails!
}
public function test_過ぎた締切に対して正しい残り時間が返る ()
{
......
}
}
결론적으로 상술한 테스트test_未来の締切に対して正しい残り時間が返る
는 실패했다.왜 그랬을까?DateInterval#days 정보
DateInterval
류에 days
속성이 존재한다.공식 매뉴얼
DateInterval
의 페이지에는 다음과 같다.days
DateTime: diff()로 작성된 DateInterval 객체의 시작일과 끝일 사이의 일수입니다.이외의 경우에는 매일 FALSE입니다.
무슨 말을 하고 있는 기분이지만, 어쨌든 그런 게 존재한다.위의 테스트를 수행하면
expected
와actual
의 diff에 days
가 나타난다는 것을 알 수 있다.PHPUnit의assertEquals
는 서로 다른 대상을 인용해도 같은 값으로 판정할 수 있지만 각 속성의 내용은 시종 같은 값이어야 한다.이런 상황을 피하는 방법은 매우 간단하다. 예를 들어
expected
명백한 diff
을 되돌려 주는 함수를 만들 수 있다.<?php
private static function _get1DayInterval ()
{
$sooner = DateTimeImmutable::createFromFormat('Y-m-d h:i:s', '2000-01-01 00:00:00');
$later = DateTimeImmutable::createFromFormat('Y-m-d h:i:s', '2000-01-02 00:00:00');
return $sooner->diff($later);
}
......
$this->assertEquals(
self::_get1DayInterval(),
$item->getRemainingInterval($now)
); // Pass!
축하합니다!잘 됐다.나 울 것 같애.또 다른 함정
다만, 대상자 간 비교 외에 다른 방법도 검토할 수 있다는 의견도 있다.
예를 들어 규격상 하루 단위의 diff만 가능한 경우(절대 일주일이나 한 달도 안 되고 끝수도 발생하지 않는 hour 등)는 처음부터 비교
DateInterval#d
만 하는 방법이 있었다.<?php
public function test_未来の締切に対して正しい残り時間が返る ()
{
$now = DateTimeImmutable::createFromFormat('Y-m-d h:i:s', '2017-12-04 00:00:00');
$item = new ItemWithDeadline([
'deadline' => new DateTimeImmutable('2017-12-05 00:00:00'),
......
]);
$this->assertEquals(
DateInterval::createFromDateString('1 day')->d,
$item->getRemainingInterval($now)->d
); // Pass!
}
통과입니다.둘 다1
라서 아무 문제 없어요.근데 이거 함정이 있어.예를 들어 마감일이 지나면 테스트 사례를 추가하자.
<?php
public function test_過去の締切に対して正しい残り時間が返る ()
{
- $now = DateTimeImmutable::createFromFormat('Y-m-d h:i:s', '2017-12-04 00:00:00');
+ $now = DateTimeImmutable::createFromFormat('Y-m-d h:i:s', '2017-12-06 00:00:00');
$item = new ItemWithDeadline([
'deadline' => new DateTimeImmutable('2017-12-05 00:00:00'),
......
]);
$this->assertEquals(
- DateInterval::createFromDateString('1 day')->d,
+ DateInterval::createFromDateString('-1 day')->d,
$item->getRemainingInterval($now)->d
); // fail!
}
실패합니다.뭔데?DateImmutable#invert 정보
DateImmutable
에는 또 하나의 재미있는 속성invert
이 있다.이것은 공식에서 다음과 같이 설명한다.invert
간격이 음수일 때는 1이고 그렇지 않으면 0이다.DateInterval: format()를 참조하십시오.
즉, 다음과 같이 어느 것이 다음 날짜인지 판단할 수 있다.편하네.
<?php
$interval = $today->diff($deadline);
if ($interval->invert == 1) {
throw new Exception('締め切りを過ぎています');
}
new DateInverval의 invert 동작
그럼 이쪽
invert
에서 상당히 재미있는 행동을 하세요.방금 $today->diff($deadline)
를 보고 얻은 기간invert
은 1
입니다.하지만 같은'마이너스 1일'
DateInterval
을 직접 하면 어떨까.$ php -a
php > var_dump(DateInterval::createFromDateString('-1 day'));
object(DateInterval)#4 (15) {
["y"]=>
int(0)
["m"]=>
int(0)
["d"]=>
int(-1)
["h"]=>
int(0)
["i"]=>
int(0)
["s"]=>
int(0)
["weekday"]=>
int(0)
["weekday_behavior"]=>
int(0)
["first_last_day_of"]=>
int(0)
["invert"]=>
int(0)
["days"]=>
bool(false)
["special_type"]=>
int(0)
["special_amount"]=>
int(0)
["have_weekday_relative"]=>
int(0)
["have_special_relative"]=>
int(0)
}
뭐invert
네0
.대신 일수d
-1
를 표시했다.회피책
방금
days
의 회피 전략으로 정해진 날짜의 차분을 취하여 그 결과를 되돌려 주는 함수를 만드는 방법을 소개했다.내가 여기서 방법을 생각해 볼게. <?php
-private static function _get1DayInterval ()
+private static function _get1DayInterval ($invert = false)
{
+ // https://github.com/beberlei/assert
+ Assersion::boolean($invert);
$sooner = DateTimeImmutable::createFromFormat('Y-m-d h:i:s', '2000-01-01 00:00:00');
$later = DateTimeImmutable::createFromFormat('Y-m-d h:i:s', '2000-01-02 00:00:00');
- return $sooner->diff($later);
+ return $invert ? $later->diff($sooner) : $sooner->diff($later);
}
이렇게 하면 음방향DateInterval
도 테스트에 정확하게 사용할 수 있다.잘 됐다.또는
포기하고 이렇게 쓰는 게 안전할 수도 있어요.
<?php
$this->assertEquals(0, $actual->y);
$this->assertEquals(0, $actual->m);
$this->assertEquals(1, $actual->d);
$this->assertEquals(0, $actual->h);
$this->assertEquals(0, $actual->i);
$this->assertEquals(0, $actual->s);
총결산
DateInterval
정말 엄격하니까 기억하는 게 좋아요.
Reference
이 문제에 관하여(DateTimeImmutable: diff를 반환하는 함수 테스트에서 new DateInterval의 경우 복사), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://zenn.dev/f_subal/articles/phpunit_datetime텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)