/** * @param bool $isPartialMock * @param \Box\TestScribe\Mock\MockClass $mock * * @param string $constructorArgumentAsStringInCode * if the string is not empty it means partial mocking is requested. * * @param string $constructorArgumentMockingStatement * mocking statements for any objects in the constructor. * * @return string */ private function renderAMockInternal($isPartialMock, MockClass $mock, $constructorArgumentAsStringInCode, $constructorArgumentMockingStatement) { $isStaticMock = $mock->isStaticMock(); if ($isStaticMock) { $mockMethodName = 'shmock_class'; } else { $mockMethodName = 'shmock'; } $mockObjectName = $mock->getMockObjectName(); $className = $mock->getClassNameBeingMocked(); $classNameAsStringInCode = var_export($className, true); $bodyStatements = $this->renderMockObjectBody($mock); $configureMockingPropertiesStatements = "\$shmock->order_matters();"; if ($isStaticMock) { $mockObjectTypeString = 'ClassBuilderStaticClass'; // @TODO (ryang 1/29/15) : re-evaluate after // https://github.com/box/shmock/issues/7 is fixed. // Due to the bug above, dont_preserve_original_methods // will interfere will order_matters and cause the generated tests to fail // Not calling dont_preserve_original_methods will cause // the original methods not overwritten to be preserved. } else { $mockObjectTypeString = 'PHPUnitMockInstance'; if ($isPartialMock) { $configureMockingPropertiesStatements = ArrayUtil::joinNonEmptyStringsWithNewLine([$configureMockingPropertiesStatements, $constructorArgumentMockingStatement, "\$shmock->set_constructor_arguments({$constructorArgumentAsStringInCode});"], 2); } else { if ($this->isInterfaceOrAbstract($className)) { // When the class being mocked is an abstract class or an interface // all original methods have to be overwritten. // Otherwise the mocked object generated by shmock will have syntax errors // when running the generated tests. $doNotPreserveOriginalMethodsStatement = "\$shmock->dont_preserve_original_methods();"; } else { // @TODO (ryang 4/21/15) : re-evaluate after // https://github.com/box/shmock/issues/8 is fixed. $doNotPreserveOriginalMethodsStatement = ''; } $configureMockingPropertiesStatements = ArrayUtil::joinNonEmptyStringsWithNewLine([$configureMockingPropertiesStatements, "\$shmock->disable_original_constructor();", $doNotPreserveOriginalMethodsStatement], 1); } } $indentUtilObj = new IndentationUtil(); $mockPropertyStatements = $indentUtilObj->indent(2, $configureMockingPropertiesStatements); $combinedBody = ArrayUtil::joinNonEmptyStringsWithNewLine([$mockPropertyStatements, $bodyStatements], 2); $mockedObjectString = <<<TAG /** @var {$className} \${$mockObjectName} */ \${$mockObjectName} = \$this->{$mockMethodName}( {$classNameAsStringInCode}, function ( /** @var {$className}|\\Shmock\\{$mockObjectTypeString} \$shmock */ \$shmock ) { {$combinedBody} } ); TAG; return $mockedObjectString; }
/** * @param MockClass $mock * * @return MockSpec */ public function createMockSpecFromMockClass(MockClass $mock) { $objectName = $mock->getMockObjectName(); $mockedClassName = $mock->getClassNameBeingMocked(); $methodInvocations = $mock->getMethodInvocations(); $invocationSpecs = $this->convertToInvocationSpecs($methodInvocations); $mockSpec = new MockSpec($objectName, $mockedClassName, $invocationSpecs); return $mockSpec; }
/** * @param \Box\TestScribe\Mock\MockClass $mockClass * @param string $injectMockedObjectMethodName * * @return string */ private function genInjectionStatement(MockClass $mockClass, $injectMockedObjectMethodName) { $className = $mockClass->getClassNameBeingMocked(); $classNameInCode = var_export($className, true); $mockObjectName = $mockClass->getMockObjectName(); // The $mockVariableName value doesn't include '$' // add '$' in the front to make it a valid statement. $statement = sprintf("%s(%s, \$%s);", $injectMockedObjectMethodName, $classNameInCode, $mockObjectName); return $statement; }
/** * Handle intercepted calls made to the mock class instance. * * @param \Box\TestScribe\Mock\MockClass $mockClass * @param string $methodName * @param array $arguments * * @return mixed|void * @throws \Box\TestScribe\Exception\TestScribeException * @throw \RuntimeException */ public function gatherAndSaveCallInfo(MockClass $mockClass, $methodName, $arguments) { $phpClass = $mockClass->getPhpClass(); $className = $mockClass->getClassNameBeingMocked(); $methodObj = $this->methodHelper->createFromMethodName($phpClass, $methodName); $retPHPDocType = $methodObj->getReturnType(); $value = $this->inputValueGetter->get($retPHPDocType, 'return value', $className, $methodName, ''); $mockClass->saveInvocationInformation($methodObj, $arguments, $value); $returnValue = $value->getValue(); return $returnValue; }
/** * Return true if partial mocking of the class under test is selected. * * @param \Box\TestScribe\Mock\MockClass|null $mockClassUnderTest * * @return bool */ public function isClassUnderTestPartiallyMocked($mockClassUnderTest) { if ($mockClassUnderTest === null) { // Testing a static method will not create a mock class under test // since partial mocking for static methods is not supported. return false; } $nameOfClassBeingMocked = $mockClassUnderTest->getClassNameBeingMocked(); $isAbstract = $this->reflectionUtil->isAbstractClass($nameOfClassBeingMocked); if ($isAbstract) { // Always use partial mocking when testing an abstract class. // The mock class is a concrete class that can be instantiated. // The abstract class can't be instantiated. return true; } // Only use partial mocking when the other public/protected methods of the // class under test are invoked during execution of the method under test. $mockMethodInvocations = $mockClassUnderTest->getMethodInvocations(); $count = count($mockMethodInvocations); return $count !== 0; }