I'm creating a simple RESTful API in PHP for practice. I'm trying to write as testable code as possible. After reading many tutorials and watching a few videos, I still feel at a loss on how to design this.
Basically, I have set up a Router class. It is responsible for mapping url paths to controller methods.
It's easy to test the router's state after adding a route.
But, how do I test if the proper class->function(params) has been called in respond()?
I'm using PHPUnit, and I've been reading about mocks. I don't know how to use them in this context.
Currently, respond() parses a given path and request method, and calls the mapped method.
Here is my current design:
private $routes;
public function __construct() {
$routes = array();
}
/**
* Gets all current routes
*/
public function getRoutes();
/**
* Sets all routes
*/
public function setRoutes($routes);
/**
* Routes GET request
*/
public function get();
/**
* Routes POST request
*/
public function post();
/**
* Routes PUT request
*/
public function put();
/**
* Routes DELETE request
*/
public function delete();
/**
* Sets up default paths for a given resource
*/
public function resource();
/**
* Respond to request
* #return mixed JSON of resource data
*/
public function respond($req_method, $request);
/**
* Returns mapped call info from request method and path
* #return mixed An array of the call and params
*/
private function parse($req_method, $request);
As the comments have indicated, this would be a good use of a spy or mocking system. PHPunit has some ability to create and test for mocks, but I prefer to use a different library - Mockery.
This code will create a class and test that that when you call average(), it's called three times, and then also returns a given value.
Your code would call respond(), after setting up a mock that shouldReceive('get')->once() (for example). The m::close in the teardown will verify that get() was called, just one time, and would fail the test if it was not.
<?php
use \Mockery as m;
class TemperatureTest extends PHPUnit_Framework_TestCase
{
public function tearDown()
{
m::close();
}
public function testGetsAverageTemperatureFromThreeServiceReadings()
{
$service = m::mock('service');
$service->shouldReceive('readTemp')->times(3)->andReturn(10, 12, 14);
$temperature = new Temperature($service);
$this->assertEquals(12, $temperature->average());
}
}
Since you would be calling a real class to do work in respond(), you will need a 'partial mock'
$restful= m::mock('RestfulController[get]');
$restful->shouldReceive('get')->once();
Related
I'm using PhpUnit/DbUnit to create a set of 5 to 10 fixture records. I use in-memory sqlite.
I successfully can use a Doctrine\DBAL\Connection to access it, so I can use methods like ->insert( $tableName, $data ); for my tests.
Now I want to consume the Doctrine EntityManager so I can create an entity and call ->persist().
In unit-testing I used to mock the EntityManager and asserted an expectation to call persist.
But for functional-testing I want to check the final result written to the DB, even go further and re-use the result of the writing.
I therefore need to create a real EntityManager but consuming the DbUnit connection.
I've seen that creting a new EntityManager takes 3 arguments, but still the creator is protected:
/**
* Creates a new EntityManager that operates on the given database connection
* and uses the given Configuration and EventManager implementations.
*
* #param \Doctrine\DBAL\Connection $conn
* #param \Doctrine\ORM\Configuration $config
* #param \Doctrine\Common\EventManager $eventManager
*/
protected function __construct(Connection $conn, Configuration $config, EventManager $eventManager)
{
[...]
I don't know if I'm expected to create a subclass, or maybe use an already subclassed EntityManager or how should I proceed.
So question:
How can I setup a real Doctrine\ORM\EntityManager within a PhpUnit/DbUnit test class using the low level Doctrine\Dbal\Connection that is already bound to the DbUnit connection?
Note: I'm inside a symfony3 project, so mapping files are in the expected places of symfony.
Trying to write some proper AEM integration tests using the aem-mocks framework. The goal is to try and test a servlet by calling its path,
E.g. an AEM servlet
#SlingServlet(
paths = {"/bin/utils/emailSignUp"},
methods = {"POST"},
selectors = {"form"}
)
public class EmailSignUpFormServlet extends SlingAllMethodsServlet {
#Reference
SubmissionAgent submissionAgent;
#Reference
XSSFilter xssFilter;
public EmailSignUpFormServlet(){
}
public EmailSignUpFormServlet(SubmissionAgent submissionAgent, XSSFilter xssFilter) {
this.submissionAgent = submissionAgent;
this.xssFilter = xssFilter;
}
#Override
public void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException {
String email = request.getParameter("email");
submissionAgent.saveForm(xssFilter.filter(email));
}
}
Here is the corresponding test to try and do the integration testing. Notice how I've called the servlet's 'doPost' method, instead of 'POST'ing via some API.
public class EmailSignUpFormServletTest {
#Rule
public final AemContext context = new AemContext();
#Mock
SubmissionAgent submissionAgent;
#Mock
XSSFilter xssFilter;
private EmailSignUpFormServlet emailSignUpFormServlet;
#Before
public void setup(){
MockitoAnnotations.initMocks(this);
Map<String,String> report = new HashMap<>();
report.put("statusCode","302");
when(submissionAgent.saveForm(any(String.class)).thenReturn(report);
}
#Test
public void emailSignUpFormDoesNotRequireRecaptchaChallenge() throws IOException {
// Setup test email value
context.request().setQueryString("email=test.only#mail.com");
//===================================================================
/*
* WHAT I END UP DOING:
*/
// instantiate a new class of the servlet
emailSignUpFormServlet = new EmailSignUpFormServlet(submissionAgent, xssFilter);
// call the post method (Simulate the POST call)
emailSignUpFormServlet.doPost(context.request(),context.response());
/*
* WHAT I WOULD LIKE TO DO:
*/
// send request using some API that allows me to do post to the framework
// Example:
// context.request().POST("/bin/utils/emailSignUp") <--- doesn't exist!
//===================================================================
// assert response is internally redirected, hence expected status is a 302
assertEquals(302,context.response().getStatus());
}
}
I've done a lot of research on how this could be done (here) and (here), and these links show a lot about how you can set various parameters for context.request() object. However, they just don't show how to finally execute the 'post' call.
What you are trying to do is mix a UT with IT so this won't be easy at least with the aem-mocks framework. Let me explain why.
Assuming that you are able to call your required code
/*
* WHAT I WOULD LIKE TO DO:
*/
// send request using some API that allows me to do post to the framework
// Example:
// context.request().POST("/bin/utils/emailSignUp") <--- doesn't exist!
//===================================================================
Your test will end up executing all the logic in SlingAllMethodsServlet class and its parent classes. I am assuming that this is not what you want to test as these classes are not part of your logic and they already have other UT/IT (under respective Apache projects) to cater for testing requirements.
Also, looking at your code, bulk of your core logic resides in following snipper
String email = request.getParameter("email");
submissionAgent.saveForm(xssFilter.filter(email));
Your UT criteria is already met by the following line of your code:
emailSignUpFormServlet.doPost(context.request(),context.response());
as it covers most of that logic.
Now, if you are looking for proper IT for posting the parameters and parsing them all the way down to doPost method then aem-mocks is not the framework for that because it does not provide it in a simple way.
You can, in theory, mock all the layers from resource resolver, resource provider and sling servlet executors to pass the parameters all the way to your core logic. This can work but it won't benefit your cause because:
Most of the code is already tested via other UT
Too many internal mocking dependencies might make the tests flaky or version dependant.
If you really want to do pure IT, then it will be easier to host the servlet in an instance and access it via HttpClient. This will ensure that all the layers are hit. A lot of tests are done this way but it feels a bit heavy handed for the functionality you want to test and there are better ways of doing it.
Also the reason why context.request().POST doesn't exist is because context.request() for is a mocked state for the sake of testing. You want to actually bind and mock Http.Post operations which needs some way to resolve to your servlet and that is not supported by the framework.
Hope this helps.
i have just implemented my first service in Symfony2.
I noticed that, within a controller method, whether i call the service so
$this->container->get('main.service');
or so
$this->get('main.service');
there is no difference.
I get the service equally with both.
Where are the differences?
There's no difference if you're extending the Base Controller provided by Symfony.
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class YourController extends Controller
If you take a deeper look at the implementation of the Symfony\Bundle\FrameworkBundle\Controller\Controller, you may notice that it provides a get() helper which do exactly the same call as what you did first (getting your service through the container).
So, then,
There's no difference as $this->get('something') simply sencapsulates a call to $this->container->get('something').
Here's the implementation of the get() method you're calling when doing $this->get('main.service');
/**
* Gets a service by id.
*
* #param string $id The service id
*
* #return object The service
*/
public function get($id)
{
return $this->container->get($id);
}
I'm using typo3 6 with extbase and some dependency injection features.
MyClass is injected with a service. The property which holds the service is protected.
class MyClass {
/**
*
* #var \X\Y\Z\MyService
* #inject
*/
protected $myService;
}
How can I change (or mock) the service in my UnitTest by?
I use reflection api to inject mock object to a protected field. See http://php.net/manual/en/class.reflectionobject.php
Im reading "The art of unit testing" atm and im having some issues with using properties to pass in an interface. The book states the following: "If you want parameters to be optional, use property getters/setters, which is a better way of defining optional parameters than adding different constructors to the class for each dependency."
The code for the property example is as follows:
public class LogAnalyzer
{
private IExtensionManager manager;
public LogAnalyzer ()
{
manager = new FileExtensionManager();
}
public IExtensionManager ExtensionManager
{
get { return manager; }
set { manager = value; }
}
public bool IsValidLogFileName(string fileName)
{
return manager.IsValid(fileName);
}
}
[Test]
Public void
IsValidFileName_NameShorterThan6CharsButSupportedExtension_ReturnsFalse()
{
//set up the stub to use, make sure it returns true
...
//create analyzer and inject stub
LogAnalyzer log = new LogAnalyzer ();
log.ExtensionManager=someFakeManagerCreatedEarlier;
//Assert logic assuming extension is supported
...
}
When/how would i use this feature?? The only scenario i can think of (This is probably wrong!) is if i had two methods in one class,
Method1() retrieves the database connection string from the config file and contains some form of check on the retrieved string.
Method2() then connect to the database and returns some data. The check here could be that that returned data is not null?
In this case, to test Method1() i could declare a stub that implements the IExtensionManager Interface, where the stub has a string which should pass any error checks i have in method1().
For Method2(), i declare a stub which implements the interface, and declare a datatable which contains some data, in the stub class. id then use the properties to assign this to the private manager variable and then call Method2?
The above may be complete BS, so if it is, id appreciate it if someone would let me know and ill remove it.
Thanks
Property injection used to change object's behavior after it was created.
BTW your code is tight coupled to FileExtensionManager, which is concrete implementation of IExtensionManager. How you are going to test LogAnalyzer with default manager? Use constructor injection to provide dependencies to your objects - this will make them testable:
public LogAnalyzer (IExtensionManager manager)
{
this.manager = manager();
}