44
55namespace Rector \PhpSpecToPHPUnit \Rector \Class_ ;
66
7+ use PhpParser \Node \Stmt \ClassMethod ;
8+ use PhpParser \Node \Identifier ;
79use PhpParser \Node ;
810use PhpParser \Node \Expr ;
911use PhpParser \Node \Expr \Assign ;
1214use PhpParser \Node \Expr \PropertyFetch ;
1315use PhpParser \Node \Expr \Variable ;
1416use PhpParser \Node \Stmt \Class_ ;
17+ use PhpParser \Node \Stmt \Property ;
18+ use PhpParser \NodeFinder ;
1519use Rector \PhpSpecToPHPUnit \Enum \PhpSpecMethodName ;
1620use Rector \PhpSpecToPHPUnit \Enum \PHPUnitMethodName ;
1721use Rector \PhpSpecToPHPUnit \Enum \ProphecyPromisesToPHPUnitAssertMap ;
1822use Rector \PhpSpecToPHPUnit \Naming \PhpSpecRenaming ;
1923use Rector \PhpSpecToPHPUnit \Naming \SystemMethodDetector ;
2024use Rector \PhpSpecToPHPUnit \NodeFactory \AssertMethodCallFactory ;
2125use Rector \PhpSpecToPHPUnit \NodeFactory \BeConstructedWithAssignFactory ;
26+ use Rector \PhpSpecToPHPUnit \NodeFactory \SetUpInstanceFactory ;
27+ use Rector \PhpSpecToPHPUnit \ValueObject \TestedObject ;
2228use Rector \Rector \AbstractRector ;
29+ use Rector \ValueObject \MethodName ;
2330use Symplify \RuleDocGenerator \ValueObject \CodeSample \CodeSample ;
2431use Symplify \RuleDocGenerator \ValueObject \RuleDefinition ;
2532
2835 */
2936final class PromisesToAssertsRector extends AbstractRector
3037{
38+ private NodeFinder $ nodeFinder ;
39+
3140 public function __construct (
3241 private readonly PhpSpecRenaming $ phpSpecRenaming ,
3342 private readonly AssertMethodCallFactory $ assertMethodCallFactory ,
3443 private readonly BeConstructedWithAssignFactory $ beConstructedWithAssignFactory ,
44+ private readonly SetUpInstanceFactory $ setUpInstanceFactory ,
3545 ) {
46+ $ this ->nodeFinder = new NodeFinder ();
3647 }
3748
3849 /**
@@ -57,6 +68,8 @@ public function refactor(Node $node): Node|null
5768
5869 $ localMethodNames = $ this ->getLocalMethodNames ($ node );
5970
71+ $ needsSetUp = true ;
72+
6073 foreach ($ node ->getMethods () as $ classMethod ) {
6174 if (! $ classMethod ->isPublic ()) {
6275 continue ;
@@ -67,9 +80,15 @@ public function refactor(Node $node): Node|null
6780 $ classMethod ,
6881 [PhpSpecMethodName::LET , PhpSpecMethodName::LET_GO , PhpSpecMethodName::GET_MATCHERS ]
6982 )) {
83+ $ needsSetUp = false ;
7084 continue ;
7185 }
7286
87+ // if all methods have this call, no need to add setUp()
88+ if ($ this ->hasBeConstructedMethodCall ($ classMethod )) {
89+ $ needsSetUp = false ;
90+ }
91+
7392 $ this ->traverseNodesWithCallable ($ classMethod , function (Node $ node ) use (
7493 $ class ,
7594 $ testedObjectPropertyFetch ,
@@ -158,7 +177,7 @@ public function refactor(Node $node): Node|null
158177 }
159178
160179 // it's a local method call, skip
161- if ($ class ->getMethod ($ methodName ) instanceof Node \ Stmt \ ClassMethod) {
180+ if ($ class ->getMethod ($ methodName ) instanceof ClassMethod) {
162181 return null ;
163182 }
164183
@@ -178,6 +197,16 @@ public function refactor(Node $node): Node|null
178197 return null ;
179198 }
180199
200+ // add setUp() method with the property
201+ if ($ needsSetUp && ! $ class ->getMethod (MethodName::SET_UP )) {
202+ $ testedObject = $ this ->phpSpecRenaming ->resolveTestedObject ($ node );
203+ $ setUpClassMethod = $ this ->setUpInstanceFactory ->createSetUpClassMethod ($ testedObject );
204+
205+ $ testedObjectProperty = $ this ->createTestedObjectProperty ($ testedObject );
206+
207+ $ class ->stmts = array_merge ([$ testedObjectProperty , $ setUpClassMethod ], $ class ->stmts );
208+ }
209+
181210 return $ node ;
182211 }
183212
@@ -247,4 +276,29 @@ private function getLocalMethodNames(Class_ $class): array
247276 /** @var string[] $localMethodNames */
248277 return $ localMethodNames ;
249278 }
279+
280+ private function createTestedObjectProperty (TestedObject $ testedObject ): Property
281+ {
282+ return $ this ->nodeFactory ->createPrivatePropertyFromNameAndType (
283+ $ testedObject ->getPropertyName (),
284+ $ testedObject ->getTestedObjectType ()
285+ );
286+ }
287+
288+ private function hasBeConstructedMethodCall (ClassMethod $ classMethod ): bool
289+ {
290+ $ methodCall = $ this ->nodeFinder ->findFirst ((array ) $ classMethod ->stmts , function (Node $ node ): bool {
291+ if (! $ node instanceof MethodCall) {
292+ return false ;
293+ }
294+
295+ if (! $ node ->name instanceof Identifier) {
296+ return false ;
297+ }
298+
299+ return $ node ->name ->toString () === 'beConstructedWith ' ;
300+ });
301+
302+ return $ methodCall instanceof MethodCall;
303+ }
250304}
0 commit comments