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.
Related
Reading an article Evil Unit testing, I am not exactly sure WHY the unit tests should never use DB, network or file systems. What if it an network app?
Unit tests are used to test the functionality of the smallest unit of your code. They should not have any dependency on any external resource that might change in future.
To take an example, let's say today you write a unit test that test a method that performs addition of two numbers.
public void AddNumberTest()
{
int a = 4; // Assume 4 coming from a database.
int b = 5; // Assume 5 coming from a database.
int c = a + b;
Assert.True(9, c);
}
This will run today. That's totally cool.
Let's say tomorrow you come and change the functionality of the Add method.
Now you should be able to run the test and it should be pass. But let's assume somehow the database server (or external resource ) is down. Then the test fail.
Even if someone changes the values in database, you need to be aware of the change to be made to the test.
Is that what you want??? Absolutely not. Right
That's why we write unit test cases that should be independent of external resources. That where we use mocks and stubs.
You should be able to run unit test a thousand times and it should give the same results always.
Unit testing is not intended to test the complete functionality of an application. It is meant to make sure that definable modules of code work as expected. To test an application it is not enough to use unit tests. You must also perform functional testing and regression testing. Database access falls outside the scope of unit testing, so you would not write unit tests that include database access. You would include database access testing in your functional tests. Similarly, if you have a network app, you would not include network access in your unit tests. In both cases you would unit test the code that manipulates the data that it gets from the db or the network using simulated-known data and leave the actual access out of it.
To reiterate, it is not enough to just unit test an app.
I'm developing a project based on Symfony2 and PHPUnit. As far as I know functional tests represents what the system should do in a user perspective. But, I have some questions about this approach.
Suppose I'm testing an user registration form. So the first thing that I should to do after submit the form is to assert that the response was successful, an email was sent and probably make assertions on success page. Internally the system should store the registration date, change the user status and etc.
The question is: checking this low level of code like registration date and status should be covered by functional tests? If not, where is the best place to put this kind of tests?
if you are not forced to include it in integration tests then i recommend doing it as a unit tests. in general your tests structure should be a pyramid: http://martinfowler.com/bliki/TestPyramid.html
i would divide this test into a few:
unit test(s) to check if the information is correctly deserialized in expected way and expected data structure is created
unit test(s) to check if that data structure is passed to specific services (e.g. if repository receives 'save' request with expected time and other paraemters)
integration test to check if repository correctly handles 'save' request. this is often a 'general' test, not tightly related to this specific user story.
general (not related to this specific user story) integration test to check if unit-tested infrastructure is connected correctly (including view/UI)
this way you will get many unit tests and only a few integration/ui tests. and that's good. that's because maintaining functional tests requires a lot of effort and are very slow comparing to unit tests
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).
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