Assuming I have a controller method that looks something like this:
public function someRoute()
{
if(some condition) {
return View::make('view1');
}
return View::make('view2');
}
How would I assert in my unit test that view1 was returned as opposed to view2? A colleague mentioned that if I can get the HTML response as a string then I can just use PHPUnit's assertRegExp against the returned HTML to match a given string only found in view1, but that doesn't seem right to me.
Is there a better way? A deeper question may be should I even need to worry about which view is returned in my unit test or if I should just $this->assertResponseOk() ?
You are mixing unit test with acceptance tests, so you have two options:
1) Split those tests to unit and acceptance, and use a tool like Codeception to help you do acceptance, which is way more elegant than PHPUnit for this kind of test. With Codecption you would do things like:
$I->amOnPage('/someUrl');
$I->see('John Doe');
2) Do what your friend told you to do.
Related
please can anyone help me understand what a macro is in Laravel Macroable trait, reading this documentation https://laravel.com/api/5.4/Illuminate/Support/Traits/Macroable.html only tells me how to use but why do I use it, what is it meant for.
It is for adding functionality to a class dynamically at run time.
use Illuminate\Support\Collection;
Collection::macro('someMethod', function ($arg1 = 1, $arg2 = 1) {
return $this->count() + $arg1 + $arg2;
});
$coll = new Collection([1, 2, 3]);
echo $coll->someMethod(1, 2);
// 6 = 3 + (1 + 2)
echo $coll->someMethod();
// 5 = 3 + (1 + 1)
We have 'macroed' some functionality to the Collection class under the name someMethod. We can now call this method on the Collection class and use its functionality.
We just added a method to the class that didn't exist before without having to touch any source files.
For more detail of what is going on, please check out my article on Macros in Laravel:
asklagbox - blog - Laravel Macros
It allows you to add new functions. One call to ::macro adds one new function. This can be done on those of the internal framework classes which are Macroable.
This action of adding the function to the class is done at run time. Note there was/is an already existing perfectly good name for this action, which isn't the word "macro", which I'll explain at the end of this post.
Q. Why would you do this?
A. If you find yourself juggling with these internal classes, like
request & response, adding a function to them might make your code more
readable.
But as always there is a complexity cost in any
abstraction, so only do it if you feel pain.
This article contains a list of the classes you can add functions to using the static call "::macro"
Try not to swallow the word macro though, if you read that article - if you're like me it will give you big indigestion.
So, let's now add one extra function to an internal framework class. Here is the example I have just implemented:
RedirectResponse::macro('withoutQuery', function() {
return redirect()->to(explode('?', url()->previous())[0]);
});
This enables me in a controller to do this:
redirect()->back()->withoutQuery();
(You can just do back() but I added redirect() to make it clear).
This example is to redirect back and where the previous route was something like:
http://myapp.com/home?something=something-else
this function removes the part after '?', to redirect to simply:
http://myapp.com/home
I did not have to code it this way. Indeed another other way to achieve this is for me to put the following function in the base class which all controllers inherit from (App\Http\Controllers\Controller).
public function redirectBackWithoutQuery()
{
return redirect()->to(explode('?',url()->previous())[0]);
}
That means I can in any controller do this:
return $this->redirectBackWithoutQuery();
So in this case the "macro" lets you pretend that your new function is part of an internal framework class, in this case the Illuminate/RedirectResponse class.
Personally I like you found it hard to grasp "laravel macros". I thought that because of the name they were something mysterious.
The first point is you may not need them often.
The second point is the choice of the name ::macro to mean "add a function to a class"
What is a real macro?
A true macro is a concept unique to Lisp. A macro is like a function but it builds and returns actual code which is then executed. It is possible to write a function in other languages which returns a string which you then execute as if it was code, and that would be pretty much the same thing. However if you think about it you have all of the syntax to deal with when you do that. Lisp code is actually structured in lists. A comparison might be imagine if javascript was all written as actual json. Then you could write javascript, which was json, which returned json, which the macro would then just execute. But lisp is a lot simpler than json in terms of its syntax so it is a lot easier than what you just imagined. So, a true lisp macro is one of the most beautiful and amazing things you can encounter.
So why are these add-a-function things in laravel called macros?
That's unknown to me I'm afraid, you'd have to ask the author, but I asked myself what they really do and is there already a name for that.
Monkey Patches
TL;DR laravel's ::macro could more accurately be described as monkey patch
So if using laravel ::macro calls, I personally decided to create a MonkeyPatchServiceProvider and put them all there, to reduce unnecessary confusion for myself.
I realise the name might sound a bit derogatory, but that's not intended at all.
It's simply because there's already a name for this, and we have so much terminology to deal with why not use an existing name.
public function addPic($loggedInId,$PicId){
$chatCoverPhotoObj = new COVER_PHOTO();
$out = $chatCoverPhotoObj->add($loggedInId,$PicId);
if($out)
return true;
return false;
}
Above sample code is written in php and simple add record in table COVER_PHOTO
Record : "picId" corresponding to loggedin user identified by "loggedInId".
I want know what should i write in unit test of this function.
Or for such function we should not write unit test.
Please suggest?
Firsty, if your System-/Class Under Test is using anything that is infrastructural concern -which in your case is the database- that tends to be an integration test.
Unit test are happening in complete isolation with every possible dependencies mocked/stubbed out.
The first problem I see in your code is that you new() up a dependency of your function. By this you can't really easily test your code.
I suggest that you better invert this dependency and have an interface in the constructor of your class, which can receive the concrete implementation of that interface. Once this is done, you can mock out the database part in your test. However by looking at the code you have shared in your question I'm not really convinced what behaviour you are going to test here.
Always make sure what you want to test for, in this case like persistence specification or the mapping configuration (of course in case you are using some sort of an OR/M). Just checking the return value -which is the success or failure flag- of the persistence operation doesn't add much business value to test against.
I'm trying to test a method that creates 2+ Process objects (also see API docs) using Symfony's ProcessBuilder and I've run into problems when trying to return different mock process objects. In fact, I'm a bit unsure about whether Mockery can even do this.
Is it possible to choose the value in Mockery's andReturn() based on the call chain of an object (including arguments)?
Ideally I'm looking for something like this:
<?php
$processBuilderMock
->shouldReceive('setPrefix("test")->add("-f")->getProcess')
->andReturn($testProcess);
Full example:
The following code creates a file /tmp/dummy if it does not exist. It uses the two commands test -f /tmp/dummy and touch /tmp/dummy for that (I know it's a dumb example).
<?php
class Toggler
{
public function toggleFile(ProcessBuilder $builder)
{
$testProcess = $builder
->setPrefix('test')->setArguments(array('-f', '/tmp/dummy'))
->getProcess();
$testProcess->run();
if (!$testProcess->isSuccessful()) { // (a)
$touchProcess = $builder
->setPrefix('touch')->setArguments(array('/tmp/dummy'))
->getProcess();
$touchProcess->run();
return $touchProcess->isSuccessful(); // (b)
} else {
// ...
}
}
}
To test all cases, I need to be able to mock the Process objects for the corresponding commands test and touch (here: $testProcess and $touchProcess). Ideally, the test code for this would look like this:
<?php
public function testToggleFileFileDoesNotExist()
{
$testProcess = \Mockery::mock('\Symfony\Component\Process\Process');
$testProcess->shouldReceive('isSuccessful')->andReturn(false); // (a)
$testProcess->shouldReceive('run');
$touchProcess = \Mockery::mock('\Symfony\Component\Process\Process');
$touchProcess->shouldReceive('isSuccessful')->andReturn(false); // (b)
$touchProcess->shouldReceive('run');
$builder = \Mockery::mock('\Symfony\Component\Process\ProcessBuilder');
$builder->shouldReceive('setPrefix("test")->getProcess')->andReturn($testProcess); // (c) Doesn't work!
$builder->shouldReceive('setPrefix("touch")->getProcess')->andReturn($touchProcess); // (c) Doesn't work!
$toggler = new Toggler();
$this->assertTrue($toggler->toggleFile($builder)); // see (b)
}
However, since Mockery does not allow arguments in the call chain, I'm a bit lost on how to test such a scenario. Any ideas?
This is slightly terrible, but you could try entering each step of the call chain separately and having it return the mock $builder. Which process is returned at the end would depend on the order of the calls.
$builder->shouldRecieve('setPrefix')->with('test')->andReturn($builder);
$builder->shouldRecieve('setPrefix')->with('touch')->andReturn($builder);
$builder->shouldRecieve('getProcess')
->andReturnValues(array($testProcess, $touchProcess));
This feels very messy, but it's the only way I've been able to find so far. (If you came up with anything better since posting this, I'd be curious to see.)
quick example of dataProvider:
return [
['180d-1pc', '6m-1pc'],
]
and test:
public function test_convert($title, $expected)
{
$uut = new Converter();
$this->assertEquals($expected, $uut->convertDayTitle($title));
}
(simply put: test if we convert 180d(days) to 6m(months))
as you can see - in data provider there are input data defined and also an expected output.
This works fine in many cases, but I keep having this feeling that maybe it's not the best idea. So I'm wondering if could be considered bad practice. If so - when I will see it was a bad idea to do it?
One example is, when you would like to use the same dataProvider in two tests - should you define two expected values and use one of them?
Quick example (I just made that up, so don't pay attention that I make product object just from title ;)):
public function test_gets_discount_when_licence_period_longer_than_1year($title, $expectedTitle, $expectedDiscount) {
$prod = new Product($title);
$this->assertEquals($expectedDiscount, $product->hasDiscount();
}
How to make this more elegant?
What you're doing is totally fine. Data providers can, and should, include the expected test result.
About the issue of reusing the same dataProvider for 2 tests, and only using some fields: I'd say that if the fields are related (i.e. they are properties of the same object), it can be acceptable.
If you feel the dataProvider is getting too big and complex because 2 tests must use data from it, just create 2 separate dataProviders, and use another private common method for building the common data.
I'm rewriting an existing Laravel 4 application to ensure that there is adequate testing. Long story short, I've rewritten my AccountController class using TDD methods and I'm running into a bit of a headache.
Consider the following method that renders a page including a list of users:
public function getIndex()
{
// build the view
//
return \View::make('account.list-users')
->with('users', \Sentry::getUserProvider()->findAll());
}
I'm using Smarty to render my views and Sentry for authentication.
Now, I want to write some tests like this:
public function test_getIndex()
{
// arrange
//
// set up some mocks here...
// act
//
$response = $this->client->request("GET", "/list-users");
// assert
//
// test for <table class="table">
$this->assertFalse($response->filter("table.table")==null, "table not found");
// test for some <a> tags for the "update" buttons
$element = $response->filter("td a")->first()->extract(array("href", "class", "_text"));
$this->assertTrue(strstr($element[0][0],"/my-update-url")!="");
$this->assertTrue(strstr($element[0][1],"btn btn-xs btn-success")!="");
$this->assertTrue(strstr($element[0][2],"Active")!="");
// test for some other markup...
}
I've been following Jeffrey Way's book Laravel Testing Decoded and written tests like the one above, and they work fine.
The headache comes up in the "set up some mocks here..." section. Specifically, the number of mocks I need to set up is ridiculous. This is because, as part of a larger web application, I'm using a View composer which adds data to the View model: the current user model, a menu structure, alert messages, news messages, the application version number, etc. I've cut out much of this by using a "bare bones" template for testing, but it's still a lot of stuff - to the point where I'm writing hundreds of lines of code to test this simple one-line method.
Is there a better way of doing this?
The way I see it, there are two ways of doing this:
A. The way I have been doing it
B. Mocking the \View::make call so that all of my template rendering is bypassed - something like this
public function test_getIndex()
{
// arrange
//
$userList = "this is a list of users";
$userProvider = Mockery::mock("\Cartalyst\Sentry\Users\Eloquent\Provider");
\Sentry::shouldReceive("getUserProvider")
->once()
->andReturn($userProvider);
$userProvider->shouldReceive("findAll")
->once()
->andReturn($userList);
$view = Mockery::mock("\Illuminate\View\View");
\View::shouldReceive("make")
->with("account.list-users")
->once()
->andReturn($view);
$view->shouldReceive("with")
->with("users", $userList)
->once()
->andReturn($view);
$view->shouldReceive("render")
->once()
->andReturn("results");
// act
//
$response = $this->call("GET", "/list-users");
// assert
//
$this->assertResponseOk();
}
If I take this approach, the testing is much simpler and I'm only testing the code that's actually in the controller method, but then I'm not really testing everything involved in calling that route (which might be a good thing or might not - I'm not sure) and I worry that I won't get adequate coverage.
So, what's the best way of doing this: (A), (B), or something else?
Edit
There's a fair amount of confusion on my part regarding the testing of my controller method, made clearer by #TheShiftExchange's answer & comments below. I'm going to try to address the issue here, as an edit, because it gives me a little more room to discuss the question.
Consider the second example given in the answer below:
public function testMethod()
{
$this->call('GET', '/list-users');
$this->assertViewHas('users', \Sentry::getUserProvider()->findAll());
}
If I run this test, it will work, but it will access the database, which I was trying to avoid by mocking some stuff.
So, I could expand this test a little:
public function testMethod()
{
\Sentry::shouldReceive("getUserProvider")
->once()
->andReturn($userProvider);
// plus a mock of the UserProvider class,...
$this->call('GET', '/list-users');
$this->assertViewHas('users', \Sentry::getUserProvider()->findAll());
}
This test will not work because, in addition to the mocks that are required for the controller method, I'll also need mocks for the code in my View composer. This code includes, among other things, $currentUser = \Sentry::getUser() (the user's name is displayed in the upper right hand corner of my application's pages).
So the code actually becomes:
public function testMethod()
{
\Sentry::shouldReceive("getUserProvider")
->once()
->andReturn($userProvider);
// plus a mock of the UserProvider class,...
// plus a mock of ThisClass
// and a mock of ThatClass
// and a mock of SomeOtherClass
// etc.
// etc.
$this->call('GET', '/list-users');
$this->assertViewHas('users', \Sentry::getUserProvider()->findAll());
}
And it quickly gets out of hand.
This suggests to me that I'm doing something wrong, but I'm not sure what. I suspect that the problem stems from my uncertainty about what exactly I'm testing here.
So, after all that, the question becomes this:
What am I really trying to test when I'm testing a controller's methods?
The code in the controller's method? Or,
The whole process from request to response?
What I want to test is the first item - just the code in the controller method. The example in my question is pretty simple, but I do have some controller methods that do things like form validation or redirect based on user input - I'd like to test that code.
Maybe, rather than testing the code via $this->call(), I need to simply call the controller method directly?
As part of the Laravel framework, it includes some testing helpers. Including in these helpers are view testing helpers:
Asserting A View Has Some Data
public function testMethod()
{
$this->call('GET', '/');
$this->assertViewHas('name');
$this->assertViewHas('age', $value);
}
So you could do something like:
public function testMethod()
{
\Sentry::shouldReceive("getUserProvider")
->once()
->andReturn('foo');
$this->call('GET', '/');
$this->assertViewHas('users', 'foo');
}