I'm about to delve into test in the PHP world and I have some questions. I have a controller which handles a loan application. The bulk of the work is then delegated to a ProcessLoanApplication class.
ApplyController
class ApplyController extends Controller
{
public function indexAction(Request $request)
{
$form = $this->createForm(new LoanApplication());
if($request->getMethod() == 'POST') {
$form->bind($request);
if($form->isValid()) {
$session = $this->getRequest()->getSession();
$loan_app_processor = new Tasks\ProcessLoanApplication($form, $session);
$loan_app_processor->process();
return $this->redirect($this->generateUrl('apply_thanks'));
}
}
Tasks\ProcessLoanApplication
class ProcessLoanApplication
{
private $_QuickBaseModels;
private $_session;
private $_app; // submitted form data
private $_existingApp = false; // holds existing application in QB, if it exists
public function __construct(Form $form, Session $session)
{
$this->_app = $form->getNormData();
$this->_session = $session;
// save the form data to a session
$session->set('application', $this->_app);
// create the quickbase table models objects
$this->_QuickBaseModels['GenFnHome\Application'] = new GenFnHome\Application();
$this->_QuickBaseModels['GenFnHome\SSN'] = new GenFnHome\SSN();
}
public function process()
{
$this->_existingApp = $this->_getExistingApplication();
$app_status = $this->_existingApp[GenFnHome\SSN::LogInApplicationStatus];
if(!$this->_existingApp || ($this->_existingApp && ($app_status !== 'PENDING' && $app_status !== 'OPEN' && $app_status !== 'EXPIRED')))
return $this->_saveNewLoanApplication();
if($app_status == 'EXPIRED') $this->_reOpenApplication();
}
There's a lot going on here, so I will outline it first:
User makes a requests for the application
Application form is validated
If valid, process loan application
Check if the user already has an app, if so - do X, if not Y
The application is persisted in an 'online database' (QuickBase) that my application communicates with via XML over HTTP (in other words, there is no real db)
My questions to the community:
What should be tested here? I know it is largely up to me, but perhaps the community can recommend some baseline tests that should def be written. Should I be testing the controller, the processor class, and the QuickBase class?
Should my tests be independent of one another - meaning, I should test each component individually, rather than have one massive testApplication that does everything the indexAction does and just looks for the expected sessions vars that get set?
Finally, how does one test API calls (request / response) without actually making real request (I'm using PHPUnit).
Anything else I should know?
Thanks!
What should be tested here? I know it is largely up to me, but perhaps the community can recommend some baseline tests that should def be written. Should I be testing the controller, the processor class, and the QuickBase class?
I recommend to test every class you build. If you are using Test Driven Development, the test declares what you are building, no test no code.
Should my tests be independent of one another - meaning, I should test each component individually, rather than have one massive testApplication that does everything the indexAction does and just looks for the expected sessions vars that get set?
Every Unit test should be isolated and should only test the Class that you are testing. You should use Mock object (use the PHPunit mock library or other 3th party libraries as Mockery) if one object dependences on another object.
Finally, how does one test API calls (request / response) without actually making real request (I'm using PHPUnit).
You can use the Symfony's WebTestCase that provides easy methods to imitate a browser request, learn more about that in the documentation. We call that Functional Testing.
This is usually the stage after Unit Testing. In Unit Testing you will test each individual class (it's a good practice to unit test your controller) and after that you write your Functional Tests which combines everything and tests if it works like expected.
for Controllers you should use functional tests (http://symfony.com/doc/2.0/book/testing.html#functional-tests). With them you can emulate browser and user's actions like submiting form and checking validation, database changes, http status codes and so on.
You should not forget to unit test ProcessLoanApplication.
I dont realy know why you pass form object to ProcessLoanApplication anyway. You should pass entity - it has normdata already.
Related
I am having issues while testing my grails controllers, as it depends on one service which seems not to be injected. I tried several ways (for ex. Extending classess like grailsunitestcase, specification) but I keep getting errors. The thing is that that service variable is null and I cant test my controller index method (which calls a render view) due to the exception...
I really need to know how to do this but I don't have a clue where to start...
Unit tests are just that. There is no grails 'environment' surrounding your controller. If the controller makes use of a service which is normally injected, you have to mock that service yourself.
#TestFor(SomeController)
#Mock([SomeService])
class SomeControllerSpec extends Specification
def "test some method"() {
given:
def mockService = mockFor(SomeService)
mockService.demand.someServiceMethod() { ->
return something
}
controller.someService = mockService.createMock()
when:
controller.someControllerMethod()
then:
// whatever checks are appropriate
}
}
First up, where my knowledge is at:
Unit Tests are those which test a small piece of code (single methods, mostly).
Integration Tests are those which test the interaction between multiple areas of code (which hopefully already have their own Unit Tests). Sometimes, parts of the code under test requires other code to act in a particular way. This is where Mocks & Stubs come in. So, we mock/stub out a part of the code to perform very specifically. This allows our Integration Test to run predictably without side effects.
All tests should be able to be run stand-alone without data sharing. If data sharing is necessary, this is a sign the system isn't decoupled enough.
Next up, the situation I am facing:
When interacting with an external API (specifically, a RESTful API that will modify live data with a POST request), I understand we can (should?) mock out the interaction with that API (more eloquently stated in this answer) for an Integration Test. I also understand we can Unit Test the individual components of interacting with that API (constructing the request, parsing the result, throwing errors, etc). What I don't get is how to actually go about this.
So, finally: My question(s).
How do I test my interaction with an external API that has side effects?
A perfect example is Google's Content API for shopping. To be able to perform the task at hand, it requires a decent amount of prep work, then performing the actual request, then analysing the return value. Some of this is without any 'sandbox' environment.
The code to do this generally has quite a few layers of abstraction, something like:
<?php
class Request
{
public function setUrl(..){ /* ... */ }
public function setData(..){ /* ... */ }
public function setHeaders(..){ /* ... */ }
public function execute(..){
// Do some CURL request or some-such
}
public function wasSuccessful(){
// some test to see if the CURL request was successful
}
}
class GoogleAPIRequest
{
private $request;
abstract protected function getUrl();
abstract protected function getData();
public function __construct() {
$this->request = new Request();
$this->request->setUrl($this->getUrl());
$this->request->setData($this->getData());
$this->request->setHeaders($this->getHeaders());
}
public function doRequest() {
$this->request->execute();
}
public function wasSuccessful() {
return ($this->request->wasSuccessful() && $this->parseResult());
}
private function parseResult() {
// return false when result can't be parsed
}
protected function getHeaders() {
// return some GoogleAPI specific headers
}
}
class CreateSubAccountRequest extends GoogleAPIRequest
{
private $dataObject;
public function __construct($dataObject) {
parent::__construct();
$this->dataObject = $dataObject;
}
protected function getUrl() {
return "http://...";
}
protected function getData() {
return $this->dataObject->getSomeValue();
}
}
class aTest
{
public function testTheRequest() {
$dataObject = getSomeDataObject(..);
$request = new CreateSubAccountRequest($dataObject);
$request->doRequest();
$this->assertTrue($request->wasSuccessful());
}
}
?>
Note: This is a PHP5 / PHPUnit example
Given that testTheRequest is the method called by the test suite, the example will execute a live request.
Now, this live request will (hopefully, provided everything went well) do a POST request that has the side effect of altering live data.
Is this acceptable? What alternatives do I have? I can't see a way to mock out the Request object for the test. And even if I did, it would mean setting up results / entry points for every possible code path that Google's API accepts (which in this case would have to be found by trial and error), but would allow me the use of fixtures.
A further extension is when certain requests rely on certain data being Live already. Using the Google Content API as an example again, to add a Data Feed to a Sub Account, the Sub Account must already exist.
One approach I can think of is the following steps;
In testCreateAccount
Create a sub-account
Assert the sub-account was created
Delete the sub-account
Have testCreateDataFeed depend on testCreateAccount not having any errors
In testCreateDataFeed, create a new account
Create the data feed
Assert the data feed was created
Delete the data feed
Delete the sub-account
This then raises the further question; how do I test the deletion of accounts / data feeds? testCreateDataFeed feels dirty to me - What if creating the data feed fails? The test fails, therefore the sub-account is never deleted... I can't test deletion without creation, so do I write another test (testDeleteAccount) that relies on testCreateAccount before creating then deleting an account of its own (since data shouldn't be shared between tests).
In Summary
How do I test interacting with an external API that effects live data?
How can I mock / stub objects in an Integration test when they're hidden behind layers of abstraction?
What do I do when a test fails and the live data is left in an inconsistent state?
How in code do I actually go about doing all this?
Related:
How can mocking external services improve unit tests?
Writing unit tests for a REST-ful API
This is more an additional answer to the one already given:
Looking through your code, the class GoogleAPIRequest has a hard-encoded dependency of class Request. This prevents you from testing it independently from the request class, so you can't mock the request.
You need to make the request injectable, so you can change it to a mock while testing. That done, no real API HTTP requests are send, the live data is not changed and you can test much quicker.
I've recently had to update a library because the api it connects to was updated.
My knowledge isn't enough to explain in detail, but i learnt a great deal from looking at the code. https://github.com/gridiron-guru/FantasyDataAPI
You can submit a request as you would normally to the api and then save that response as a json file, you can then use that as a mock.
Have a look at the tests in this library which connects to an api using Guzzle.
It mocks responses from the api, there's a good deal of information in the docs on how the testing works it might give you an idea of how to go about it.
but basically you do a manual call to the api along with any parameters you need, and save the response as a json file.
When you write your test for the api call, send along the same parameters and get it to load in the mock rather than using the live api, you can then test the data in the mock you created contains the expected values.
My Updated version of the api in question can be found here.
Updated Repo
One of the ways to test out external APIs is as you mentioned, by creating a mock and working against that with the behavior hard coded as you have understood it.
Sometimes people refer to this type of testing as "contract based" testing, where you can write tests against the API based on the behavior you have observed and coded against, and when those tests start failing, the "contract is broken". If they are simple REST based tests using dummy data you can also provide them to the external provider to run so they can discover where/when they might be changing the API enough that it should be a new version or produce a warning about not being backwards compatible.
Ref: https://www.thoughtworks.com/radar/techniques/consumer-driven-contract-testing
I have this simple method which calls the TFS (Team foundation server) API to get WorkItemCollection object. I have just converted in to an entity class and also added it in cache. As you can see this is very simple.
How should i unit test this method. Only the important bit it does is calls TFS API. Is it worth testing such methods? If yes then how should we test it?
One way I can think is I can mock call to Query.QueryWorkItemStore(query) and return an object of type “WorkItemCollection” and see finally this method converts “WorkItemCollection” to List. And check if it was added to cache or not.
Also as I am using dependency injection pattern her so I am injecting dependency for
cache
Query
Should I only pass dependency of mocked type (Using MOQ) or I should pass proper class type.
public virtual List<Sprint> Sprint(string query)
{
List<Sprint> sprints =
Cache.Get<List<Sprint>>(query);
if (sprints == null)
{
WorkItemCollection items =
Query.QueryWorkItemStore(query);
sprints = new List<Sprint>();
foreach (WorkItem i in items)
{
Sprint sprint = new Sprint
{
ID = i.Id,
IterationPath = i.IterationPath,
AreaPath = i.AreaPath,
Title = i.Title,
State = i.State,
Goal = i.Description,
};
sprints.Add(sprint);
}
Cache.Add(sprints, query,
this.CacheExpiryInterval);
}
return sprints;
}
Should I only pass dependency of mocked type (Using MOQ) or I should pass proper class type.
In your unit tests, you should pass a mock. There are several reasons:
A mock is transparent: it allows you to check that the code under test did the right thing with the mock.
A mock gives you full control, allowing you to test scenarios that are difficult or impossible to create with the real server (e.g. throw IOException)
A mock is predictable. A real server is not - it may not even be available when you run your tests.
Things you do on a mock don't influence the outside world. You don't want to change data or crash the server by running your tests.
A test with mocks is faster. No connection to the server or real database queries have to be made.
That being said, automated integration tests which include a real server are also very useful. You just have to keep in mind that they will have lower code coverage, will be more fragile, and will be more expensive to create/run/maintain. Keep your unit tests and your integration tests separate.
edit: some collaborator objects like your Cache object may also be very unit-test friendly. If they have the same advantages as that of a mock that I list above, then you don't need to create a mock. For example, you typically don't need to mock a collection.
This is a tough one because not too many people use Pex & Moles or so I think (even though Pex is a really great product - much better than any other unit testing tool)
I have a Data project that has a very simple model with just one entity (DBItem). I've also written a DBRepository within this project, that manipulates this EF model. Repository has a method called GetItems() that returns a list of business layer items (BLItem) and looks similar to this (simplified example):
public IList<BLItem> GetItems()
{
using (var ctx = new EFContext("name=MyWebConfigConnectionName"))
{
DateTime limit = DateTime.Today.AddDays(-10);
IList<DBItem> result = ctx.Items.Where(i => i.Changed > limit).ToList();
return result.ConvertAll(i => i.ToBusinessObject());
}
}
So now I'd like to create some unit tests for this particular method. I'm using Pex & Moles. I created my moles and stubs for my EF object context.
I would like to write parametrised unit test (I know I've first written my production code, but I had to, since I'm testing Pex & Moles) that tests that this method returns valid list of items.
This is my test class:
[PexClass]
public class RepoTest
{
[PexMethod]
public void GetItemsTest(ObjectSet<DBItem> items)
{
MEFContext.ConstructorString = (#this, name) => {
var mole = new SEFContext();
};
DBRepository repo = new DBRepository();
IList<BLItem> result = repo.GetItems();
IList<DBItem> manual = items.Where(i => i.Changed > DateTime.Today.AddDays(-10));
if (result.Count != manual.Count)
{
throw new Exception();
}
}
}
Then I run Pex Explorations for this particular parametrised unit test, but I get an error path bounds exceeded. Pex starts this test by providing null to this test method (so items = null). This is the code, that Pex is running:
[Test]
[PexGeneratedBy(typeof(RepoTest))]
[Ignore("the test state was: path bounds exceeded")]
public void DBRepository_GetTasks22301()
{
this.GetItemsTest((ObjectSet<DBItem>)null);
}
This was additional comment provided by Pex:
The test case ran too long for these inputs, and Pex stopped the analysis. Please notice: The method Oblivious.Data.Test.Repositories.TaskRepositoryTest.b__0 was called 50 times; please check that the code is not stuck in an infinite loop or recursion. Otherwise, click on 'Set MaxStack=200', and run Pex again.
Update attribute [PexMethod(MaxStack = 200)]
Question
Am I doing this the correct way or not? Should I use EFContext stub instead? Do I have to add additional attributes to test method so Moles host will be running (I'm not sure it does now). I'm running just Pex & Moles. No VS test or nUnit or anything else.
I guess I should probably set some limit to Pex how many items should it provide for this particular test method.
Moles is not designed to test the parts of your application that have external dependencies (e.g. file access, network access, database access, etc). Instead, Moles allows you to mock these parts of your app so that way you can do true unit testing on the parts that don't have external dependencies.
So I think you should just mock your EF objects and queries, e.g., by creating in-memory lists and having query methods return fake data from those lists based on whatever criteria is relevant.
I am just getting to grips with pex also ... my issues surrounded me wanting to use it with moq ;)
anyway ...
I have some methods similar to your that have the same problem. When i increased the max they went away. Presumably pex was satisfied that it had sufficiently explored the branches. I have methods where i have had to increase the timeout on the code contract validation also.
One thing that you should probably be doign though is passing in all the dependant objects as parameters ... ie dont instantiate the repo in the method but pass it in.
A general problem you have is that you are instantiating big objects in your method. I do the same in my DAL classes, but then i am not tryign to unit test them in isolation. I build up datasets and use this to test my data access code against.
I use pex on my business logic and objects.
If i were to try and test my DAL code id have to use IOC to pass the datacontext into the methods - which would then make testing possible as you can mock the data context.
You should use Entity Framework Repository Pattern: http://www.codeproject.com/KB/database/ImplRepositoryPatternEF.aspx
I've read a lot of Zend controller testing tutorials but I can't find one that explains how to test a controller that uses models and mocking those models.
I have the following controller action:-
function indexAction(){
// Get the cache used by the application
$cache = $this->getCache();
// Get the index service client and model
$indexServiceClient = new IndexServiceClient($this->getConfig());
$indexModel = $this->_helper->ModelLoader->load('admin_indexmodel', $cache);
$indexModel->setIndexServiceClient($indexServiceClient);
// Load all the indexes
$indexes = $indexModel->loadIndexes();
$this->view->assign('indexes', $indexes);
}
At the moment I have a very basic test case:-
public function testIndexActionRoute() {
$this->dispatch( '/admin/index' );
$this->assertModule('admin', 'Incorrect module used');
$this->assertController('index', 'Incorrect controller used');
$this->assertAction('index', 'Incorrect action used');
}
This test works but it's calling the real models and services, which sometimes means it times out and fails on the test environment. In order to properly unit test just the controller I need to have mocks and expectations for IndexServiceClient and IndexModel - how is this done?
Well, since not many replies I am seeing here, I'll try to add my 2cents(potentially arguable).
Answer written below is my IHMO and very subjective(and I think not very useful, but here we go anyway)
I think controllers are not a good fit unit testing. Your business logic layer, models etc. is what is unitestable.
Controllers are connected with UI and bring system together so to speak - hence to me they are a better fit for integration and UI testing - something that packages such as Selenium are used for.
To my mind testing should be easy enough to implement such that total effort for testing implementation is adequate to the returns of it. Wiring up all dependencies for a controller seems to me(with my limited knowledge of course) a bit too much of a task.
The other way to think about it is - what is actually going on in your controllers. Again IHMO it supposed to be primarily a glue level between your business logic and your UI. If you're putting a lot of business logic into controller it will have an adverse effect(for instance it won't be easily unitestable..).
This is all sort of theory of course. Hopefully someone can provide a better answer and actually show how to easily wire up a controller for unit tests!
One possible solution that a colleague put forward is to use a Zend Controller Action Helper to inject mock dependencies. This should work in theory but I've yet to extensively test this method
Here is an example of doing it this way.
class Mock_IndexModel_Helper extends Zend_Controller_Action_Helper_Abstract {
private $model;
public function __construct($model) {
$this->model = $model;
}
/**
* Init hook
*/
public function init() {
$this->getActionController()->setIndexModel( $this->model );
}
}
class IndexControllerTest extends Zend_Test_PHPUnit_ControllerTestCase {
public $bootstrap = BOOTSTRAP;
public function setUp(){
parent::setUp();
}
/**
* Test the correct module/controller/action are used
*/
public function testIndexAction() {
$mockIndexModel = $this->getMock("IndexModel");
Zend_Controller_Action_HelperBroker::addHelper(new Mock_IndexModel_Helper($mockIndexModel));
$this->dispatch( '/admin/index' );
$this->assertModule('admin', 'Incorrect module used');
$this->assertController('index', 'Incorrect controller used');
$this->assertAction('index', 'Incorrect action used');
}
}