1717use Magento \Catalog \Pricing \Price \FinalPrice ;
1818use Magento \Framework \Pricing \Amount \AmountInterface ;
1919use Magento \Framework \Pricing \PriceCurrencyInterface ;
20+ use Magento \Framework \Pricing \PriceInfoInterface as FrameworkPriceInfoInterface ;
2021use Magento \Framework \TestFramework \Unit \Helper \MockCreationTrait ;
2122use Magento \Tax \Ui \DataProvider \Product \Listing \Collector \Tax ;
2223use PHPUnit \Framework \MockObject \MockObject ;
2324use PHPUnit \Framework \TestCase ;
2425
26+ /**
27+ * Unit test for Tax collector
28+ *
29+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
30+ */
2531class TaxTest extends TestCase
2632{
2733 use MockCreationTrait;
2834
2935 /**
3036 * @var Tax
3137 */
32- protected $ model ;
38+ private Tax $ model ;
3339
3440 /**
3541 * @var PriceCurrencyInterface|MockObject
3642 */
37- protected $ priceCurrencyMock ;
43+ private MockObject $ priceCurrencyMock ;
3844
3945 /**
4046 * @var PriceInfoInterface|MockObject
4147 */
42- private $ priceMock ;
48+ private MockObject $ renderPriceInfoMock ;
4349
4450 /**
4551 * @var PriceInfoInterfaceFactory|MockObject
4652 */
47- private $ priceInfoFactory ;
53+ private MockObject $ priceInfoFactory ;
4854
4955 /**
5056 * @var PriceInfoExtensionInterface|MockObject
5157 */
52- private $ extensionAttributes ;
58+ private MockObject $ extensionAttributes ;
5359
5460 /**
5561 * @var PriceInfoExtensionInterfaceFactory|MockObject
5662 */
57- private $ priceInfoExtensionFactory ;
63+ private MockObject $ priceInfoExtensionFactory ;
5864
5965 /**
6066 * @var FormattedPriceInfoBuilder|MockObject
6167 */
62- private $ formattedPriceInfoBuilder ;
68+ private MockObject $ formattedPriceInfoBuilder ;
69+
70+ /**
71+ * @var FrameworkPriceInfoInterface|MockObject
72+ */
73+ private MockObject $ frameworkPriceInfoMock ;
6374
6475 protected function setUp (): void
6576 {
6677 $ this ->priceCurrencyMock = $ this ->createMock (PriceCurrencyInterface::class);
6778
68- // PriceInfoInterface: The test needs to mock getPrice() which doesn't exist in the interface
69- // This is a test design issue - ideally should mock a concrete implementation
70- // Workaround: Using getMockBuilder()->getMock() which allows any method configuration in PHPUnit 12
71- $ this ->priceMock = $ this ->getMockBuilder (PriceInfoInterface::class)
72- ->disableOriginalConstructor ()
73- ->getMock ();
74-
75- // Note: PriceInfoExtensionInterface is a generated extension attribute interface
76- // Using createMock would fail as the interface doesn't exist until DI compilation
77- // Keeping original approach but migrated to PHPUnit 12
78- $ this ->extensionAttributes = $ this ->getMockBuilder (PriceInfoExtensionInterface::class)
79- ->disableOriginalConstructor ()
80- ->getMock ();
79+ // PriceInfoInterface (Catalog API) - used for product render DTO
80+ $ this ->renderPriceInfoMock = $ this ->createMock (PriceInfoInterface::class);
81+
82+ // PriceInfoExtensionInterface is a generated extension attribute interface
83+ // All interface methods must be included when using createPartialMockWithReflection
84+ $ this ->extensionAttributes = $ this ->createPartialMockWithReflection (
85+ PriceInfoExtensionInterface::class,
86+ [
87+ // All interface methods from generated interface
88+ 'getMsrp ' ,
89+ 'setMsrp ' ,
90+ 'getTaxAdjustments ' ,
91+ 'setTaxAdjustments ' ,
92+ 'getWeeeAttributes ' ,
93+ 'setWeeeAttributes ' ,
94+ 'getWeeeAdjustment ' ,
95+ 'setWeeeAdjustment '
96+ ]
97+ );
8198
8299 $ this ->priceInfoFactory = $ this ->getMockBuilder (PriceInfoInterfaceFactory::class)
83100 ->disableOriginalConstructor ()
@@ -88,9 +105,11 @@ protected function setUp(): void
88105 ->disableOriginalConstructor ()
89106 ->onlyMethods (['create ' ])
90107 ->getMock ();
91- $ this ->formattedPriceInfoBuilder = $ this ->getMockBuilder (FormattedPriceInfoBuilder::class)
92- ->disableOriginalConstructor ()
93- ->getMock ();
108+
109+ $ this ->formattedPriceInfoBuilder = $ this ->createMock (FormattedPriceInfoBuilder::class);
110+
111+ // Framework PriceInfoInterface - has getPrice() method, returned by Product::getPriceInfo()
112+ $ this ->frameworkPriceInfoMock = $ this ->createMock (FrameworkPriceInfoInterface::class);
94113
95114 $ this ->model = new Tax (
96115 $ this ->priceCurrencyMock ,
@@ -101,79 +120,119 @@ protected function setUp(): void
101120 }
102121
103122 /**
123+ * Test collect method
124+ *
104125 * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
105126 */
106127 public function testCollect (): void
107128 {
108- $ this ->markTestSkipped (
109- 'Test requires mocking getPrice() method which does not exist in PriceInfoInterface. ' .
110- 'PHPUnit 12 removed addMethods() and getMockForAbstractClass() methods, ' .
111- 'making it impossible to mock non-existent methods without test refactoring. ' .
112- 'This test needs to be refactored to mock a concrete implementation that has getPrice() method. '
113- );
114-
115129 $ amountValue = 10 ;
116130 $ minAmountValue = 5 ;
117131 $ storeId = 1 ;
118132 $ currencyCode = 'usd ' ;
119133
120134 $ productMock = $ this ->createMock (Product::class);
121135 $ productRender = $ this ->createMock (ProductRenderInterface::class);
122- $ price = $ this ->createMock (FinalPrice::class);
123- // PriceInfoInterface: Using the mock already created in setUp() instead of creating a new one
124- // This avoids the issue of mocking getPrice() which doesn't exist in the interface
125- $ priceInfo = $ this ->priceMock ;
136+ $ finalPrice = $ this ->createMock (FinalPrice::class);
137+ $ regularPrice = $ this ->createMock (FinalPrice::class);
126138 $ amount = $ this ->createMock (AmountInterface::class);
127139 $ minAmount = $ this ->createMock (AmountInterface::class);
140+ $ maxAmount = $ this ->createMock (AmountInterface::class);
128141
129- $ priceInfo ->expects ($ this ->exactly (4 ))
130- ->method ('getPrice ' )
131- ->willReturn ($ price );
132- $ this ->priceInfoFactory ->expects ($ this ->once ())
133- ->method ('create ' )
134- ->willReturn ($ priceInfo );
135- $ productRender ->expects ($ this ->once ())
136- ->method ('getPriceInfo ' )
137- ->willReturn ($ priceInfo );
142+ // Product::getPriceInfo() returns Framework's PriceInfoInterface (has getPrice())
138143 $ productMock ->expects ($ this ->any ())
139144 ->method ('getPriceInfo ' )
140- ->willReturn ($ priceInfo );
141- $ priceInfo ->expects ($ this ->once ())
142- ->method ('getExtensionAttributes ' )
143- ->willReturn ($ this ->extensionAttributes );
144- $ priceInfo ->expects ($ this ->atLeastOnce ())
145- ->method ('getPrice ' )
146- ->willReturn ($ price );
147-
148- $ price ->expects ($ this ->once ())
149- ->method ('getMaximalPrice ' )
145+ ->willReturn ($ this ->frameworkPriceInfoMock );
146+
147+ // Framework PriceInfo's getPrice() returns price objects
148+ $ this ->frameworkPriceInfoMock ->method ('getPrice ' )
149+ ->willReturnCallback (function ($ priceCode ) use ($ finalPrice , $ regularPrice ) {
150+ if ($ priceCode === 'final_price ' ) {
151+ return $ finalPrice ;
152+ }
153+ if ($ priceCode === 'regular_price ' ) {
154+ return $ regularPrice ;
155+ }
156+ return $ finalPrice ;
157+ });
158+
159+ // Setup price amounts
160+ $ finalPrice ->expects ($ this ->atLeastOnce ())
161+ ->method ('getAmount ' )
150162 ->willReturn ($ amount );
151- $ price ->expects ($ this ->once ())
163+ $ finalPrice ->expects ($ this ->once ())
164+ ->method ('getMaximalPrice ' )
165+ ->willReturn ($ maxAmount );
166+ $ finalPrice ->expects ($ this ->once ())
152167 ->method ('getMinimalPrice ' )
153168 ->willReturn ($ minAmount );
154- $ price ->expects ($ this ->exactly (2 ))
169+
170+ $ regularPrice ->expects ($ this ->once ())
155171 ->method ('getAmount ' )
156172 ->willReturn ($ amount );
157- $ amount ->expects ($ this ->exactly (3 ))
173+
174+ $ amount ->expects ($ this ->atLeastOnce ())
158175 ->method ('getValue ' )
176+ ->with (['tax ' , 'weee ' ])
159177 ->willReturn ($ amountValue );
178+
179+ $ maxAmount ->expects ($ this ->once ())
180+ ->method ('getValue ' )
181+ ->with (['tax ' , 'weee ' ])
182+ ->willReturn ($ amountValue );
183+
160184 $ minAmount ->expects ($ this ->once ())
161185 ->method ('getValue ' )
186+ ->with (['tax ' , 'weee ' ])
162187 ->willReturn ($ minAmountValue );
188+
189+ // ProductRender::getPriceInfo() returns Catalog API's PriceInfoInterface (DTO)
190+ $ productRender ->expects ($ this ->once ())
191+ ->method ('getPriceInfo ' )
192+ ->willReturn ($ this ->renderPriceInfoMock );
193+
194+ // PriceInfo factory creates new PriceInfo DTOs
195+ $ newPriceInfo = $ this ->createMock (PriceInfoInterface::class);
196+ $ this ->priceInfoFactory ->expects ($ this ->once ())
197+ ->method ('create ' )
198+ ->willReturn ($ newPriceInfo );
199+
200+ // Setup extension attributes
201+ $ this ->renderPriceInfoMock ->expects ($ this ->once ())
202+ ->method ('getExtensionAttributes ' )
203+ ->willReturn ($ this ->extensionAttributes );
204+
205+ // Extension attributes setTaxAdjustments is called
206+ $ this ->extensionAttributes ->expects ($ this ->once ())
207+ ->method ('setTaxAdjustments ' )
208+ ->with ($ newPriceInfo );
209+
210+ // Setup price info setters on new DTO
211+ $ newPriceInfo ->expects ($ this ->once ())->method ('setFinalPrice ' )->with ($ amountValue );
212+ $ newPriceInfo ->expects ($ this ->once ())->method ('setMaxPrice ' )->with ($ amountValue );
213+ $ newPriceInfo ->expects ($ this ->once ())->method ('setMinimalPrice ' )->with ($ minAmountValue );
214+ $ newPriceInfo ->expects ($ this ->once ())->method ('setSpecialPrice ' );
215+ $ newPriceInfo ->expects ($ this ->once ())->method ('setRegularPrice ' )->with ($ amountValue );
216+ $ newPriceInfo ->method ('getFinalPrice ' )->willReturn ($ amountValue );
217+
218+ $ this ->renderPriceInfoMock ->expects ($ this ->once ())
219+ ->method ('setExtensionAttributes ' )
220+ ->with ($ this ->extensionAttributes );
221+
163222 $ productRender ->expects ($ this ->once ())
164223 ->method ('setPriceInfo ' )
165- ->with ($ priceInfo );
224+ ->with ($ this -> renderPriceInfoMock );
166225
167226 $ productRender ->expects ($ this ->once ())
168227 ->method ('getStoreId ' )
169- ->willReturn (1 );
228+ ->willReturn ($ storeId );
170229 $ productRender ->expects ($ this ->once ())
171230 ->method ('getCurrencyCode ' )
172231 ->willReturn ($ currencyCode );
173232
174233 $ this ->formattedPriceInfoBuilder ->expects ($ this ->once ())
175234 ->method ('build ' )
176- ->with ($ priceInfo , $ storeId , $ currencyCode );
235+ ->with ($ newPriceInfo , $ storeId , $ currencyCode );
177236
178237 $ this ->model ->collect ($ productMock , $ productRender );
179238 }
0 commit comments