@@ -71,6 +71,30 @@ public function isZero関数でゼロかどうかを判定できる(): void
7171 $ this ->assertFalse ($ nonZeroValue ->isZero ());
7272 }
7373
74+ #[Test]
75+ public function isPositive関数で正の値かどうかを判定できる (): void
76+ {
77+ $ positiveValue = TestDecimalValue::from (new Number ('123.45 ' ));
78+ $ negativeValue = TestDecimalValue::from (new Number ('-123.45 ' ));
79+ $ zeroValue = TestDecimalValue::from (new Number ('0 ' ));
80+
81+ $ this ->assertTrue ($ positiveValue ->isPositive ());
82+ $ this ->assertFalse ($ negativeValue ->isPositive ());
83+ $ this ->assertFalse ($ zeroValue ->isPositive ());
84+ }
85+
86+ #[Test]
87+ public function isNegative関数で負の値かどうかを判定できる (): void
88+ {
89+ $ positiveValue = TestDecimalValue::from (new Number ('123.45 ' ));
90+ $ negativeValue = TestDecimalValue::from (new Number ('-123.45 ' ));
91+ $ zeroValue = TestDecimalValue::from (new Number ('0 ' ));
92+
93+ $ this ->assertFalse ($ positiveValue ->isNegative ());
94+ $ this ->assertTrue ($ negativeValue ->isNegative ());
95+ $ this ->assertFalse ($ zeroValue ->isNegative ());
96+ }
97+
7498 /**
7599 * @return array<string, array{string}>
76100 */
@@ -621,6 +645,77 @@ public function 有効桁数を超える値はエラーになることを確認(
621645 $ this ->assertStringContainsString ('30 ' , $ errorMessage , 'エラーメッセージには実際の桁数(30)が含まれるべき ' );
622646 }
623647
648+ // ------------------------------------------
649+ // DecimalValueFactoryのtryFromメソッドのテスト
650+ // ------------------------------------------
651+
652+ #[Test]
653+ public function tryFromメソッドは未実装のため常にResult_okを返す (): void
654+ {
655+ // DecimalValueFactoryのtryFromメソッドは抽象メソッドなので
656+ // 実装クラスで実装される必要がある
657+ $ value = new Number ('100.50 ' );
658+ $ result = TestDecimalValue::tryFrom ($ value );
659+
660+ $ this ->assertTrue ($ result ->isOk ());
661+ $ this ->assertEquals ('100.50 ' , (string )$ result ->unwrap ()->value );
662+ }
663+
664+ #[Test]
665+ public function tryFromメソッドは範囲外の値に対してエラーを返す (): void
666+ {
667+ // 範囲外の値をテスト
668+ $ result = TestDecimalValue::tryFrom (new Number ('1001 ' ));
669+ $ this ->assertFalse ($ result ->isOk ());
670+ $ this ->assertInstanceOf (ValueObjectError::class, $ result ->unwrapErr ());
671+ }
672+
673+ #[Test]
674+ public function tryFromメソッドは有効桁数を超える値に対してエラーを返す (): void
675+ {
676+ // 範囲内で有効桁数を超える値をテスト
677+ $ largeDigitValue = '1. ' . str_repeat ('9 ' , 29 ); // 30桁の値(範囲内)
678+ $ result = TestDecimalValue::tryFrom (new Number ($ largeDigitValue ));
679+
680+ $ this ->assertFalse ($ result ->isOk ());
681+ $ this ->assertInstanceOf (ValueObjectError::class, $ result ->unwrapErr ());
682+
683+ $ errorMessage = $ result ->unwrapErr ()->getMessage ();
684+ $ this ->assertStringContainsString ('桁数 ' , $ errorMessage );
685+ }
686+
687+ /**
688+ * @return array<string, array{string, bool}>
689+ */
690+ public static function tryFrom用のテストデータを提供 (): array
691+ {
692+ return [
693+ '有効な正の値 ' => ['100.50 ' , true ],
694+ '有効な負の値 ' => ['-100.50 ' , true ],
695+ '有効なゼロ値 ' => ['0 ' , true ],
696+ '有効な最小値 ' => ['-1000 ' , true ],
697+ '有効な最大値 ' => ['1000 ' , true ],
698+ '無効な最小値未満 ' => ['-1000.01 ' , false ],
699+ '無効な最大値超過 ' => ['1000.01 ' , false ],
700+ '有効桁数内の値 ' => ['123.456789 ' , true ],
701+ ];
702+ }
703+
704+ #[Test]
705+ #[DataProvider('tryFrom用のテストデータを提供 ' )]
706+ public function tryFromメソッドのデータ駆動テスト (string $ value , bool $ shouldSucceed ): void
707+ {
708+ $ result = TestDecimalValue::tryFrom (new Number ($ value ));
709+
710+ if ($ shouldSucceed ) {
711+ $ this ->assertTrue ($ result ->isOk (), "値 {$ value } は有効であるべき " );
712+ $ this ->assertEquals ($ value , (string )$ result ->unwrap ()->value );
713+ } else {
714+ $ this ->assertFalse ($ result ->isOk (), "値 {$ value } は無効であるべき " );
715+ $ this ->assertInstanceOf (ValueObjectError::class, $ result ->unwrapErr ());
716+ }
717+ }
718+
624719 #[Test]
625720 public function formatToNumberメソッドの動作確認 (): void
626721 {
@@ -764,4 +859,193 @@ public function 極端な桁数でのフォーマットテスト(): void
764859 $ this ->assertEquals ('123 ' , $ value ->format (0 ));
765860 $ this ->assertEquals ('123 ' , (string )$ value ->formatToNumber (0 ));
766861 }
862+
863+ // ------------------------------------------
864+ // DecimalValueBaseのisValidDigitsメソッドのテスト
865+ // ------------------------------------------
866+
867+ #[Test]
868+ public function isValidDigitsメソッドは有効な桁数に対してResult_okを返す (): void
869+ {
870+ // 有効な桁数(29桁以下)
871+ $ validValue = new Number ('123.456 ' );
872+ $ result = TestDecimalValue::tryFrom ($ validValue );
873+ $ this ->assertTrue ($ result ->isOk ());
874+ }
875+
876+ #[Test]
877+ public function isValidDigitsメソッドは無効な桁数に対してResult_errを返す (): void
878+ {
879+ // 無効な桁数(30桁)で範囲内の値
880+ $ invalidValue = new Number ('1. ' . str_repeat ('9 ' , 29 ));
881+ $ result = TestDecimalValue::tryFrom ($ invalidValue );
882+ $ this ->assertFalse ($ result ->isOk ());
883+
884+ $ errorMessage = $ result ->unwrapErr ()->getMessage ();
885+ $ this ->assertStringContainsString ('桁数 ' , $ errorMessage );
886+ $ this ->assertStringContainsString ('29 ' , $ errorMessage ); // 許容桁数
887+ $ this ->assertStringContainsString ('30 ' , $ errorMessage ); // 実際の桁数
888+ }
889+
890+ /**
891+ * @return array<string, array{string, bool}>
892+ */
893+ public static function isValidDigits用のテストデータを提供 (): array
894+ {
895+ return [
896+ '有効な桁数_1桁 ' => ['1 ' , true ],
897+ '有効な桁数_2桁 ' => ['12 ' , true ],
898+ '有効な桁数_小数点含む ' => ['123.456 ' , true ],
899+ '有効な桁数_負数 ' => ['-123.456 ' , true ],
900+ '有効な桁数_ゼロ ' => ['0 ' , true ],
901+ '有効な桁数_小数点のみ ' => ['1.0 ' , true ],
902+ '無効な桁数_大きな小数 ' => ['1. ' . str_repeat ('9 ' , 29 ), false ],
903+ '無効な桁数_大きな負数 ' => ['-1. ' . str_repeat ('9 ' , 29 ), false ],
904+ ];
905+ }
906+
907+ #[Test]
908+ #[DataProvider('isValidDigits用のテストデータを提供 ' )]
909+ public function isValidDigitsメソッドのデータ駆動テスト (string $ value , bool $ shouldSucceed ): void
910+ {
911+ $ result = TestDecimalValue::tryFrom (new Number ($ value ));
912+
913+ if ($ shouldSucceed ) {
914+ $ this ->assertTrue ($ result ->isOk (), "値 {$ value } は有効な桁数であるべき " );
915+ } else {
916+ $ this ->assertFalse ($ result ->isOk (), "値 {$ value } は無効な桁数であるべき " );
917+ $ this ->assertInstanceOf (ValueObjectError::class, $ result ->unwrapErr ());
918+
919+ $ errorMessage = $ result ->unwrapErr ()->getMessage ();
920+ $ this ->assertStringContainsString ('桁数 ' , $ errorMessage );
921+ }
922+ }
923+
924+ #[Test]
925+ public function isValidDigitsメソッドは小数点とマイナス記号を除外して桁数を計算する (): void
926+ {
927+ // 小数点とマイナス記号を除外した桁数のテスト
928+ $ testCases = [
929+ '123.456 ' => 6 , // 1,2,3,4,5,6
930+ '-123.456 ' => 6 , // 1,2,3,4,5,6 (マイナス記号は除外)
931+ '0.123 ' => 4 , // 0,1,2,3
932+ '-0.123 ' => 4 , // 0,1,2,3 (マイナス記号は除外)
933+ '100.00 ' => 5 , // 1,0,0,0,0
934+ ];
935+
936+ foreach ($ testCases as $ value => $ expectedDigits ) {
937+ $ result = TestDecimalValue::tryFrom (new Number ($ value ));
938+ $ this ->assertTrue ($ result ->isOk (), "値 {$ value } は有効であるべき(桁数: {$ expectedDigits }) " );
939+ }
940+ }
941+
942+ #[Test]
943+ public function isValidDigitsメソッドは29桁ちょうどの値を受け入れる (): void
944+ {
945+ // 29桁ちょうどの値で範囲内
946+ $ exactValue = '1. ' . str_repeat ('1 ' , 27 ); // 1.111...(29桁)
947+ $ result = TestDecimalValue::tryFrom (new Number ($ exactValue ));
948+ $ this ->assertTrue ($ result ->isOk ());
949+ }
950+
951+ #[Test]
952+ public function isValidDigitsメソッドは30桁以上の値を拒否する (): void
953+ {
954+ // 30桁の値で範囲内
955+ $ overLimitValue = '1. ' . str_repeat ('9 ' , 29 ); // 1.999...(31桁)
956+ $ result = TestDecimalValue::tryFrom (new Number ($ overLimitValue ));
957+ $ this ->assertFalse ($ result ->isOk ());
958+
959+ $ errorMessage = $ result ->unwrapErr ()->getMessage ();
960+ $ this ->assertStringContainsString ('桁数 ' , $ errorMessage );
961+ $ this ->assertStringContainsString ('29 ' , $ errorMessage );
962+ $ this ->assertStringContainsString ('30 ' , $ errorMessage );
963+ }
964+
965+ // ------------------------------------------
966+ // zero()メソッドのテスト
967+ // ------------------------------------------
968+
969+ #[Test]
970+ public function zero関数でゼロの値オブジェクトを生成できる (): void
971+ {
972+ $ zeroValue = TestDecimalValue::zero ();
973+
974+ $ this ->assertInstanceOf (TestDecimalValue::class, $ zeroValue );
975+ $ this ->assertEquals ('0 ' , (string )$ zeroValue ->value );
976+ $ this ->assertTrue ($ zeroValue ->isZero ());
977+ $ this ->assertFalse ($ zeroValue ->isPositive ());
978+ $ this ->assertFalse ($ zeroValue ->isNegative ());
979+ }
980+
981+ #[Test]
982+ public function zero関数で生成したインスタンスは他のゼロ値と等価である (): void
983+ {
984+ $ zeroValue1 = TestDecimalValue::zero ();
985+ $ zeroValue2 = TestDecimalValue::from (new Number ('0 ' ));
986+ $ zeroValue3 = TestDecimalValue::from (new Number ('0.0 ' ));
987+
988+ $ this ->assertTrue ($ zeroValue1 ->equals ($ zeroValue2 ));
989+ $ this ->assertTrue ($ zeroValue1 ->equals ($ zeroValue3 ));
990+ $ this ->assertTrue ($ zeroValue2 ->equals ($ zeroValue3 ));
991+ }
992+
993+ #[Test]
994+ public function zero関数で生成したインスタンスは算術演算で期待通りに動作する (): void
995+ {
996+ $ zeroValue = TestDecimalValue::zero ();
997+ $ someValue = TestDecimalValue::from (new Number ('123.45 ' ));
998+
999+ // ゼロとの加算
1000+ $ addResult = $ someValue ->add ($ zeroValue );
1001+ $ this ->assertTrue ($ addResult ->equals ($ someValue ));
1002+
1003+ // ゼロとの減算
1004+ $ subResult = $ someValue ->sub ($ zeroValue );
1005+ $ this ->assertTrue ($ subResult ->equals ($ someValue ));
1006+
1007+ // ゼロとの乗算
1008+ $ mulResult = $ someValue ->mul ($ zeroValue );
1009+ $ this ->assertTrue ($ mulResult ->equals ($ zeroValue ));
1010+ }
1011+
1012+ /**
1013+ * @return array<string, array{string, bool, bool, bool}>
1014+ */
1015+ public static function 正負判定用のテストデータを提供 (): array
1016+ {
1017+ return [
1018+ '正の整数 ' => ['100 ' , false , true , false ],
1019+ '正の小数 ' => ['123.45 ' , false , true , false ],
1020+ '負の整数 ' => ['-100 ' , false , false , true ],
1021+ '負の小数 ' => ['-123.45 ' , false , false , true ],
1022+ 'ゼロ ' => ['0 ' , true , false , false ],
1023+ 'ゼロ小数 ' => ['0.0 ' , true , false , false ],
1024+ '正の小さい値 ' => ['0.01 ' , false , true , false ],
1025+ '負の小さい値 ' => ['-0.01 ' , false , false , true ],
1026+ '大きな正の値 ' => ['999.99 ' , false , true , false ],
1027+ '大きな負の値 ' => ['-999.99 ' , false , false , true ],
1028+ ];
1029+ }
1030+
1031+ /**
1032+ * @param string $value テスト対象の値
1033+ * @param bool $expectZero isZeroの期待値
1034+ * @param bool $expectPos isPositiveの期待値
1035+ * @param bool $expectNeg isNegativeの期待値
1036+ */
1037+ #[Test]
1038+ #[DataProvider('正負判定用のテストデータを提供 ' )]
1039+ public function 正負判定メソッドのデータ駆動テスト (
1040+ string $ value ,
1041+ bool $ expectZero ,
1042+ bool $ expectPos ,
1043+ bool $ expectNeg
1044+ ): void {
1045+ $ decimalValue = TestDecimalValue::from (new Number ($ value ));
1046+
1047+ $ this ->assertSame ($ expectZero , $ decimalValue ->isZero (), "値 {$ value } のisZero()結果が期待値と異なる " );
1048+ $ this ->assertSame ($ expectPos , $ decimalValue ->isPositive (), "値 {$ value } のisPositive()結果が期待値と異なる " );
1049+ $ this ->assertSame ($ expectNeg , $ decimalValue ->isNegative (), "値 {$ value } のisNegative()結果が期待値と異なる " );
1050+ }
7671051}
0 commit comments