trouble geting started with laravel testing and mockery - unit-testing

I'm trying to get started on unit testing with laravel and am trying to follow a couple of tutorials.
Alot of my controllers have been generated using Jeffrey Ways excellent generators and they appear to create their own tests so I thought it would be simple to get started.
I've installed mockery and sqlite - I've removed a lot of the tests from the folder for now so I can test one at a time but I'm having trouble with the first one that tests a create:
Here's my test:
<?php
use Mockery as m;
use Way\Tests\Factory;
class BooksTest extends TestCase {
public function __construct()
{
$this->mock = m::mock('Eloquent', 'Book');
$this->collection = m::mock('Illuminate\Database\Eloquent\Collection')->shouldDeferMissing();
}
public function setUp()
{
parent::setUp();
$this->attributes = Factory::book(['id' => 1]);
$this->app->instance('Book', $this->mock);
}
public function tearDown()
{
m::close();
}
public function testIndex()
{
$this->mock->shouldReceive('all')->once()->andReturn($this->collection);
$this->call('GET', 'books');
$this->assertViewHas('books');
}
public function testCreate()
{
$this->call('GET', 'books/create');
$this->assertResponseOk();
}
public function testStore()
{
$this->mock->shouldReceive('create')->once();
$this->validate(true);
$this->call('POST', 'books');
$this->assertRedirectedToRoute('books.index');
}
}
When I run phpunit I get lots of php messages around mockery - theres so much there I cant see the specifc error message
Is there a simple step I'm missing here in my set up??
I've added mockery to my composer.json and updated. I have not added anything to app.php in the config files - package didnt suggest I should or as a facade.
So I'm not sure, never having used it before whether mockery is installed correctly - although I assume it is. Was it necessary for me to add mockery to a laravel installation or is it part of a normal install?

Solved - definite PICNIC error
I needed to extend the buffer size of my command line to scroll back and see the error which identified the problem -
This is my first dip into testing so didnt quite expect the fail to go the way it did.
Tiny steps required

Related

Web Unit Tests not finding Url

I am using aspnetboilerplate 5.1.0.
In the ProjectName.Web.Tests I have run into a situation that I cannot solve.
I have set up web tests for my controller using [Fact] or [Theory].
When I attempt to run the tests using GetResponseAsString(string url, HttpStatusCode expectedStatusCode = HttpStatusCode.OK) found in the webtestbase class. All the tests fail.
Here is an example of my Test:
[Fact]
public async Task Index_Test()
{
//Act
var response = await GetResponseAsStringAsync(
GetUrl<HomeController>(nameof(HomeController.Index))
);
//Assert
response.ShouldNotBeNullOrEmpty();
}
The Tests all fail on this:
Message:
Shouldly.ShouldAssertException : response.StatusCode
should be
HttpStatusCode.OK
but was
HttpStatusCode.NotFound
I have other aspnetboilerplate projects in version 3.8.3 and 4.2.1 and the web tests work just fine. So I'm not sure why the server is not able to find the action methods on my controllers.
The service tests found in the ProjectName.Tests project run just fine.
I found the culprit. The problem I was experiencing was due to attempting to copy a project for web unit tests from one of the aspnetboilerplate project template repositories and updating all of the references and class names to match the names and namespaces in the destination VS solution.
I submitted a similar question on the aspnetboilerplate github account.
https://github.com/aspnetboilerplate/aspnetboilerplate/issues/5463.
Ultimately, here is what happened.
After going through the same process with a newer project. I found that In the
class file that would by default be named AbpProjectNameWebTestBase.cs in the method
protected override IWebHostBuilder CreateWebHostBuilder()
{
return base
.CreateWebHostBuilder()
.UseContentRoot(ContentRootFolder.Value)
.UseSetting(WebHostDefaults.ApplicationKey, typeof(AbpProjectNameWebModule).Assembly.FullName);
}
I mistakenly replaced AbpProjectNameWebModule with AbpProjectNameTestModule instead of AbpProjectNameWebMvcModule. This was trying to use the Application Service Unit test project as the web project. Therefore it could not find any of the referenced URI's and therefore returned httpStatusCode.NotFound.
After fixing this reference. I started getting exceptions that pertained to the public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) method.
These were things like adding app.UseAuthentication() and app.UseAuthorization() as well as needing to add a Middleware to provide a ClaimsIdentity and ClaimsPrincipal for the context.User (i.e. app.UserMiddleware<TestAuthenticationMiddleware>())
Now, I am able to get my web unit tests to run as I had in previous versions.

Http.Context with FakeApplication and illusive mock method

In my tests I create a fake application per test method:
#Before
public void startFakeApplication() {
this.fakeApplication = fakeApplication();
start(this.fakeApplication);
}
#After
public void killFakeApplication() {
stop(this.fakeApplication);
this.fakeApplication = null;
}
Some of the tests use functionality that checks if the request is secure or not:
public boolean isHttps() {
Http.Request req = Controller.request();
return req.getHeader("x-forwarded-proto") != null
&& req.getHeader("x-forwarded-proto").contains("https");
}
That fails saying:
There is no HTTP Context available from here
Which is pretty strange, since it's running on a fake app, why can't it know that and create a fake request?
Oh well, I found this: Play framework 2.2.1: Create Http.Context for tests which introduced me to the mocking approach, so I was eager to give it a go and try to mock the Http.Context in the same way, the problem is that I can't seem to find the mock method...
In that thread he's using import static org.mockito.Mockito.* (which is where I assume the mock method is located) but I don't have that package, org.mockito only has one sub package named internal and I can't find any mock method there.
In the official documentation of Play! the only place talking about it is the Scala Test section and they use: import org.specs2.mock._ but there too I wasn't able to locate this mock method.
I'm using Play 2.2.2 (java).
Any ideas? Thanks.
I solved the same problem adding to my build.sbt the library dependency of Mockito:
libraryDependencies += "org.mockito" % "mockito-core" % "1.10.19"
Then I run play compile and play eclipse and magically the mockito library became available after refreshing the whole project in Eclipse.
And yes, mock() is a method of org.mockito.Mockito.
I had the same problem of Play not locating the mock function, and eventually realised that I hadn't extended my test class with Mockito;
import org.specs2.mock._
class TestClass extends Specification with Mockito
Just thought I'd add this as it has taken me ages to resolve and the above solution didn't work for me ......may save someone some time :)

Laravel workbench unit test

What is the way to test my packages in workbench. If I write a unit test then no classes are autoloaded. So this means that:
<?php
use \Mockery as m;
class ExampleTest extends TestCase {
public function tearDown()
{
m::close();
}
/**
* A basic functional test example.
*
* #return void
*/
public function testShouldReturnValidServer()
{
$mock = m::mock('MailChimp[sendCurl]');
MailChimp::listSubscribe( array( 'id' => 'c79a023ff2', 'email_address' => 'dennieriechelman#gmail.com'));
}
}
results in a error saying that class TestCase is not found. When I add class TestCase to the autoload in my composer.json (the one in my package folder) the class is available. However then I get the next error that "Illuminate\Foundation\Testing\TestCase" is not available etc. etc.
So my question is what should I autoload in my composer.json in my package folder? Everything just like in my main composer.json or is there some other way that I am missing.
I know that in the manuel it says"
You may git init from the workbench/[vendor]/[package] directory and git push your package straight from the workbench! This will allow you to conveniently develop the package in an application context without being bogged down by constant composer update commands.
However I do not understand this. Can someone explain what is meant with this? By the way I am familiar with git. I just do not get the context.
EDIT1
As far I understand now is that you push your package to your repository and then include it in your main composer.json as package. I just do not see how this is helpful when developing. Hopefully I understand this wrong.. :)
EDIT2
I was wrong. You keep your package in workbench until it stable. Just like Nils pointed out below.The question still remains though. How do I create an environment in which I can unit test with the app started. I mean like testing a model where I can mock the facades etc. Or is doing this in the workbench bad practice?
I created a package for this purpose at https://github.com/orchestral/testbench
If you don't mind merging the results of your workbench testing with the results of your main application, you can simply add extra directories to your main phpunit.xml in your laravel root like this:
<testsuites>
<testsuite name="Application Test Suite">
<directory>./app/tests/phpunit/</directory>
<directory>./workbench/vendor/packageOne/tests/</directory>
<directory>./workbench/vendor/packageTwo/tests/</directory>
</testsuite>
</testsuites>
Then in the tests folder of your package, place your phpunit tests as normal, along with the TestCase.php file, adjusting the createApplication() function to be:
<?php
class TestCase extends \Illuminate\Foundation\Testing\TestCase {
public function createApplication()
{
$unitTesting = true;
$testEnvironment = 'testing';
return require './bootstrap/start.php';
}
Make sure your package composer.json auto-loads that TestCase.php file like so:
"autoload": {
"classmap": [
"tests/phpunit/TestCase.php"
]
}
Run composer dump-autoload -o to get everything aligned and then you should be able to run phpunit from your laravel root and it will test both your application and your packages.
Extend from the proper namespace and you should be able to run tests from package dir.
class ExampleTest extends \Illuminate\Foundation\Testing\TestCase {
..
}
See also Laravels neat testing helpers in workbench?

using build-test-data plugin with Grails 2

I'm trying to use the build-test-data plugin (v. 2.0.4) to build test data in a unit test of a Grails 2.1.4 application.
The app has the following domain classes
class Brochure {
static constraints = {}
static hasMany = [pageTags: PageTag]
}
class PageTag {
static constraints = {
}
static belongsTo = [brochure: Brochure]
}
Then in my unit test I try to build an instance of PageTag with
#Build([Brochure, PageTag])
class BrochureTests {
void testSomething() {
PageTag pageTag = PageTag.build()
}
}
But it fails with the error
groovy.lang.MissingMethodException: No signature of method:
btd.bug.Brochure.addToPageTags() is applicable for argument types:
(btd.bug.PageTag) values: [btd.bug.PageTag : (unsaved)] Possible
solutions: getPageTags()
My example looks exactly the same as that shown in the plugin's docs, so I've no idea why this isn't working. A sample app that demonstrates the issue is available here.
Fixed in version 2.0.5
I commented on the linked github issue, but this is because of a perf "fix" in how grails #Mock annotation works.
This change pretty much removes all of the linking code that made it possible for BTD to work in unit tests.
The only way around it currently is to also add an explict #Mock annotation for all of the domain objects in the part of the domain graph that's required to build a valid object.
The test code will be quicker with this change, which is great, but it puts a larger burden on the developer to know and maintain these relationships in their tests (which is what BTD was trying to avoid :).

Grails pollution between integration and unit tests

I know there's a lot out there about this particular topic, however I can't quite find anyone who has stumbled across my issue, and hopefully someone can explain this to me.
I have a Domain where I use the injected grailsApplication's dynamic method 'isDomainClass' in the equals method:
#Override
public boolean equals(Object obj) {
if(!grailsApplication.isDomainClass(obj.getClass())) { return false }
...
}
This works fine, and to unit test this i do:
#Mock([MyDomain])
...
def mockGApp
void setUp() {
mockGApp = new Object()
mockGApp.metaClass.isDomainClass = { obj -> true }
}
...
void testSomething() {
def myDomain = new MyDomain()
myDomain.grailsApplication = mockGApp
....
}
And when I run this with test-app -unit (on command line or in STS) it passes just fine.
I then ave an integration test that uses that domain (no mocking this time) and that again runs fine when ran with test-app -integration (either on the command line or in STS)
However if i run 'test-app' so it does both at once, I get a MissingMethodException: no method signature isDomainClass exists with parameters (java.lang.Class) ... and all that jazz.
On investigating it with println's in the service I'm testing in the tests, in the integration portion of the testing, before the equals method of my domain class is called, I can quite happily call grailsApplication.isDomainClass() and get the desired affect. However when the code steps into the domain's equals function the isDomainClass() method no longer exists, despite the grailsApplication object referring to the same object which is referenced in the service and has the dynamically added method.
It appears that the dynamic methods that grails adds to this class are not being injected when its called within the domain's methods but are getting injected within the service. And more bizarrely this only happens if the integration tests follow the unit tests. If done separately, no problemo...
Where does this pollution stem from? IS there any way to solve it?
P.S. using Grails 2.1.0
You have to remove the class you modified from metaClassRegistry in the destroy method (i.e.after test case runs). See below:
#After
void destroy() {
GroovySystem.metaClassRegistry.removeMetaClass(MyDomain.class)
}