I created a PHP class that consume's AskGeo's API. (http://askgeo.com)
I want to test it with PHPUNIT and include those tests in the PSR package I am releasing, but I don't want to give away my Account ID and API key that I will need to test the API.
What's the best way to do this?
I was thinking I could include a config file that could be filled in with those credentials before running tests.
Is that the best way?
Good unit tests must be fast and repeatable. If you make your tests work with remote API, you will break this rules:
HTTP request is too long to execute - never know how long, good tests work for milliseconds
once customer have unstable internet, test results may differ. And tests will not work without internet at all.
What you may do is to separate your code to your logic and some transport layer. You should inject transport into logic code, to be able use real transport (like cUrl) for production and mock transport for testing env. Mock transport should return answers from fixtures you provide. This will make tests extremelly fast and always produce same result.
Transport layer can look like this:
TransportInterface with method request($url, $params, $method, ...)
StubTransport implements TransportInterface with __construct($fixtures) - if $url+$params+$method found in fixtures, return result.
CurlTransport implements TransportInterface - cUrl implementation.
VoodooMagicTransport implements TransportInterface - get results using some black magic.
You logic code: FantasticLogic class with __construct(TransportInterface $transport). Do not use implementation's class name in parameter type, only interface.
In my opinion, that is the best way if you really want to test against the live production API on each test run -- to require the developer that is utilizing your class to enter in their own credentials. However, if there isn't a proper set of credentials available, you should consider using a stub (a static canned answer to the call).
Related
I'm writing an yii2 app that is mainly used as an console application. I have components or (micro)services that fetches some data from a server, handle them and save the information I need to db. Of course there are several for loops and in these loops I output my current progress with the use of yii\helpers\Console::updateProgress or just echo to watch the progress and testing output (like starting with xxx products). On some events I log important informations with Yii::info() or Yii::error() and so on. Normally a cron handling tasks like pullProductUpdates or something else and i.
However in some cases I need the method (i.e. pullProductUpdates) in my webapplication too. But then there must not be any echo command active or Console::updateProgress commands.
Of course I don't have problems with the logging methods from Yii because I configured the log targets and they will not echoing something. But I'm uncertain how to handle the echo commands...
One way is to check wether $_SERER['REMOTE_ADDR'] is set or not. In console it will evaluate to null so I can warp an if {} else {} around. A probably better solution is to write a log() or progress() method. A trait could be useful?
So how should I design a solution? Is there any pattern for this? Should my services implement an interface like loggable or proressable? Or use an Logger/ Progress objects and use some kind of DI (dependency injection)? Because I don't want to write those log() or progress() methods functions more than one time. Besides I can't use a progress function in a webapplication. One reason is I don't know how to to that (if its possible with php here), but this would be another question.
Thanks in advance.
As a PHP programmer you should be aware of and use the PSR. In this case you should use dependency injection and the LoggerInterfaces.
For web application you should configure your composition root to use a logger implementation that logs to a file. For console application you should log to the terminal.
The composition root is the place where you configure your Dependency Injection Container (DIC). See more about Yii DIC here.
In order to do that you should be able to switch between these two composition roots by an environment variable or by php_sapi_name.
TLDR: Writing a service (in the model layer). It talks to ffmpeg. Where should validation go? Should I create a service response object so it is testable? How should it be structured?
Background: I'm designing some classes to retrieve data from an external service. It could be an API, but in fact it's calls to ffmpeg cli, which in effect is an API to the conversion tools themselves.
When talking to an external service, where the data retrieved may not always be the same, how is it best to go about maintaining at least a consistent application state on your end so your application doesn't depend on the external service to work?
I have already separated out the classes thus far, trying to maintain SRP within them:
class CommandDispatcher { }
The Command Dispatcher's sole job is to make a request for data (to ffmpeg) and retrieve the response for that data back.
class Converter { }
The converter's sole responsibility is to take user requests (like convert 1 to 2), and send the basics to the command dispatcher which handles the exec() calls.
Here are my questions:
When talking to an external API / service, should I be creating an APIRequest and an APIResponse object (in this case an FFmpegResponse object)?
I have seen examples of this for OAuth, where there is an OAuth response object. However, that's simple enough because calls to this are done over the HTTP protocol which tends to give back at minimum an error code and a message. Calls to something like ffmpeg don't guarantee a similar response (ffmpeg may not be installed, for example). Is this object merely a domain object (i.e. an entity: some class members and setters and getters)?
Validation. If I am creating an FFmpegResponse object, whose job is it to put the data into the right members of the Response object?
Imagine ffmpeg isn't installed and the CommandDispatcher gets the response back empty. Is it up to the CommandDispatcher to then populate the FFmpegResponse object with an "ffmpeg not installed" error? Should I have a validation object do this?
Remember, I'm trying to stick to the Single Responsibility Principle here, so I'm thinking that the CommandDispatcher shouldn't care about whether the data is valid or not - it should merely ask for data and retrieve it. Where does my validation fit within the model layer for this service?
This isn't only for FFmpeg but will help for future external service calls. What is the best [practice] way to structure your code and classes to maintain SRP yet also a consistent application state regardless of whether or not the external service responds in an expected way?
In your current structure, CommandDispatcher should be either an interface or an abstract class (depending on the necessity of abstract code). You would then create a concrete implementation: FFMpegCommandDispatcher which would encapsulate the understanding of ffmpeg specific responses.
Response objects will then take on a similar structure: CommandResponse would be an abstraction with the concrete implementation FFMpegCommandResponse.
It would be best to create a set of common error conditions (serviceNotAvailable, serviceNotInstalled, serviceDiedAHorribleAndBloodyDeath, etc). Your dispatcher implementation can then set a common error code on the response object and provider implementation specific details. ('Error 1984: FFMpeg is watching you')
If you're concerned (and I would be) about validating input as well. You could create a CommandRequest abstraction, and FFMpegRequest implementation, that will take user input and make sure that it's okay to be sent to the command line.
I'm just beginning to Unit Test with my projects, but I still face a problem that's throwing me off. I really want to knuckle down and get to grips with Unit Testing because I know the long term benefits are worth the effort to learn.
I work extensively with the Twitter on an almost daily basis. I create a number of applications that work with Twitter.
In a real case scenario if I want to run a Unit Test to test whether the login was successful (not using the API in this case) how would I test this is true in the Unit Test? What I would usually do in production code is test to see whether the user ID is set on the page, or whether a certain cookie exists that only a logged in user would have. Then failing that I throw an exception.
But how would I test this in a Unit Test?
Would it be the same as how I would do it in production code? For example testing to see if a known cookie exists that only a logged in user has?
There are a number of scenarios like this that I really struggle to wrap my head around when thinking in terms of Unit Testing.
I know it can get kinda tricky when building applications that rely on third parties because the third party could change the way they work and your tests are useless until you've changed them.
When it comes to unittest an API or writing a cookie you can't/shouldn't test the existance of the cookie or that the API has accepted your request.
You know the format and values that are needed for a positive API request. When you test an API you usually hardcode the data to see if everything works with it. In case of your unittest you check if the needed values you normally send to the API have the expected format where the API works without errors. Your unittests are not supposed to test the API itself.
The same with cookies. You only test that the function/method is called with the expected data where a cookie is written without errors. You don't test something like a browser in a unittest.
The kind of high-level test that you manually perform on your application (e.g. manually logging in and then checking for some cookie) would probably not be called "Unit Test". Unit Tests, as the name suggests, should test small components of your application.
See this explanation of different types of test.
When you are working with external services, a common strategy is to create Mocks of their API. They allow you to check if your application is making the correct requests and is correctly processing the results - without actually connecting to the API.
An example:
Let's say your application has a class User. You can set username and password for a user and then tell it to login() to an external system. You would expect your application to make a request to that API, using the username and password that you set.
This can be roughly translated to a PHPUnit test like this:
public function testLogin() {
// We create a mock of an existing wrapper class for the API
$mockAPI = $this->getMock('ApiWrapperClass');
// Create a new User instance, passing the mock API as a dependency
$user = new User($mockApi);
$user->username = "Test user";
$user->password = "secret";
// Now we set up the expectation of what should happen
$mockApi->expects($this->once())
->method('login')
->with(
$this->equalTo("Test user"),
$this->equalTo("secret")
)
->will($this->returnValue(TRUE));
// Now we can call the actual function that we are testing
$user->login();
}
The mock API will simply return true to emulate a successful login.
The PHPUnit documentation has a chapter on Mock objects.
I have read in another document that classes which have methods with connections to a database have to be tested by integration tests. I tried to find an example, but I didn't find any, only theory. I've very little experience with testing. Can you give me an example? a link?, something (instead of only theory, because I read a lot), with something like this?. I think this is not an strange case. If its possible with php and sql or similar.
If you dont know about integration tests, how do you test this kinds of problems?
class database{
public $server;
public $database;
public $password;
public $user;
public $descriptor;
function __construct(){
$this->server="localhost";
$this->database="mydb";
$this->user="stackoverflow";
$this->password="1234";
$this->descriptor=mysql_connect($this->server,$this->user,$this->password);
mysql_select_db($this->database,$this->descriptor);
}
function make_request($query){
$response=mysql_query($query,$this->descriptor);
return $response;
}
function close_connection(){
mysql_close($this->descriptor);
}
function number_rows($result_query){
return mysql_num_rows($result_query);
}
}
Look into PHPUnit for unit testing. You can then use mock objects to simulate the dependencies (DB calls) to return dummy data. Because you're trying to test some unit of logic and not that your database layer, it should be acceptable and give you a reasonable amount of confidence that your code is working.
Another option is to use integration tests, but this requires that you put a lot of effort into the setup and teardown of your tests. You need to make sure any data required for your tests exists within your database. It's common to use transactions for these types of tests which you can then rollback after your finished running those tests.
Which code is it you're trying to test? Is it the code that uses your database class? If so, you don't need to introduce PDO, as your database class already provides an abstraction (well, it would be much safer if public $descriptor was private). So, you should start by mocking your database class and then testing the code that uses it. PHPUnit has a sophisticated mocking system.
Another thing you might want to be testing is this database class itself? In that case you'll have to accept the MySQL database dependency, and the speed hit that gives you. Set up a completely dedicated MySQL database for that testing. If you don't mind the speed hit, consider creating the database in a setUp() function and removing it after each test in a tearDown() function.
The third thing you might want to be testing is that your website (or whatever) works as a whole, and connects to the database, etc. This is functional testing (also called integration testing). If your system offers a web services API, then you could use that to test it. Otherwise you could use Selenium (still using PHPUnit) to test by remote control of a real browser. If your tests will only read from the database, you could use your live system; but it would be much more sensible to assume a write will happen at some point, so test against a test version of the website, with its own dedicated test database.
The advantage of functional testing is it high level, testing for all interactions between components, testing the system as the end user will use it. The downside is when a test fails it only tells you something is wrong, not exactly which component is to blame. So we have unit tests to test each component in isolation.
I know you this is more text, less code, than you wanted! But you have actually asked quite a theoretical question! Just try setting up some unit tests of this code, watch what problems occur over the first month of using them, and you'll understand why and where to use mocks.
I'm writing a PHPUnit test case for an API (so not exactly a unit test) and I'm thinking about having a test that all other tests will depend on.
The tests in the test case make API requests. Most of these requests require a user. The test in question will create that user that the other tests will use.
Would that be a horrible idea?
I think that the best way for unit tests is to eliminate the dependencies first.
You can abstract the end point with your own local version that will return predictible results. This way you can test that your requests are correct.
You can abstract the data providers (database, filesitem, etc...) with your stubs that will also return predictible data (username, etc..).
After that you just test your request and see they are correct..
The second part is to actualy test the data providers, with different tests, so you know that the good username will be given.
And then you can test the API connectivity, etc..
EDIT. If you have dependencies in your code, and it's difficult to abstract the providers or the end point web service, you may need to adjust your code so that it will accept references to those objects as parameters. Than in your tests you change the objects passed with your own stub objects. In production you pass the correct references, so that you will not need to change your code for testing.
I hope i have been clear. If not, ask me and i can explain better, maybe i did not understand your question well