Grails Controller Testing - Problems with dynamic methods - unit-testing

I'm running some old (but valid, i'm told) tests on a legacy application, and notice that many of them arent working. The error message usually is 'No method signature for some dymamic method'
After using mockDomain I managed to solve that problem.
However, I can't figure out how to test controllers that create objects inside.
For example, I created a sample controller (omitted import statements)
package com.tmp
class DummyController2 {
def index = { }
def createObject={
def emp= new Emp(name:'name',description:'description')
if (emp.validate()){
render 'OK'
}
else{
render 'FAIL'
}
}
}
And then the sample controllerTest
package com.tmp
class DummyController2Tests extends ControllerUnitTestCase{
DummyController2 controller
public void setUp(){
super.setUp()
controller = new DummyController2()
}
public DummyController2Tests(){
super(DummyController2Tests)
}
public void tearDown(){
super.tearDown()
}
void testCreateObject(){
assertEquals 'OK',controller.createObject()
}
}
Now when I run this test, I get the
groovy.lang.MissingMethodException: No
signature of method: Emp.validate() is
applicable for argument types: ()
values: []
Is there a workaround on this? Adding mockDomain statements inside the controller seems very intrusive and wrong. Maybe its just that I'm using an old grails (1.2.1)?
Thanks in advance

Your domain class is not mocked. Add to setUp():
mockDomain Emp

Related

Mock view helper in Zend Framework 3 in PHPUnit test

I want to test a specific controller action in Zend Framework 3. Because I use ZfcUser (https://github.com/ZF-Commons/ZfcUser) and Bjyauthorize (https://github.com/bjyoungblood/BjyAuthorize) I need to mock some view helpers. For example I need to mock isAllowed view helper and let it return true always:
class MyTest extends AbstractControllerTestCase
{
public function setUp()
{
$this->setApplicationConfig(include 'config/application.config.php');
$bootstrap = \Zend\Mvc\Application::init(include 'config/application.config.php');
$serviceManager = $bootstrap->getServiceManager();
$viewHelperManager = $serviceManager->get('ViewHelperManager');
$mock = $this->getMockBuilder(IsAllowed::class)->disableOriginalConstructor()->getMock();
$mock->expects($this->any())->method('__invoke')->willReturn(true);
$viewHelperManager->setService('isAllowed', $mock);
$this->getApplication()->getServiceManager()->setAllowOverride(true);
$this->getApplication()->getServiceManager()->setService('ViewHelperManager', $viewHelperManager);
}
public function testViewAction()
{
$this->dispatch('/myuri');
$resp = $this->getResponse();
$this->assertResponseStatusCode(200);
#$this->assertModuleName('MyModule');
#$this->assertMatchedRouteName('mymodule/view');
}
}
In my view.phtml (which will be rendered by opening/dispatching /myuri uri) I call the view helper $this->isAllowed('my-resource').
But I got response code 500 with failing exception when executing testViewAction():
Exceptions raised:
Exception 'Zend\ServiceManager\Exception\ServiceNotFoundException' with message 'A plugin by the name "isAllowed" was not found in the plugin manager Zend\View\HelperPluginManager' in ../vendor/zendframework/zend-servicemanager/src/AbstractPluginManager.php:131
How can I inject my isAllowed mock to the view helper manager in a way, that let the test case (testViewAction / $this->dispatch()) pass.
As stated in the previous answer already, we need to override the ViewHelper within the ViewHelperManager in the application object. The following code shows how this could be achieved:
public function setUp()
{
$this->setApplicationConfig(include 'config/application.config.php');
$bootstrap = \Zend\Mvc\Application::init(include 'config/application.config.php');
$serviceManager = $bootstrap->getServiceManager();
// mock isAllowed View Helper of Bjyauthorize
$mock = $this->getMockBuilder(IsAllowed::class)->disableOriginalConstructor()->getMock();
$mock->expects($this->any())->method('__invoke')->willReturn(true);
// inject the mock into the ViewHelperManager of the application
$this->getApplication()->getServiceManager()->get('ViewHelperManager')->setAllowOverride(true);
$this->getApplication()->getServiceManager()->get('ViewHelperManager')->setService('isAllowed', $mock);
}
ViewHelperManager is an other instance of service manager. and it's not allowed to override source code. Can you try "setAllowOverride" before "setService" method?

Is it possible to mock a controller and have access to the FormTagLib in the same unit test?

As the title states, is it possible to have a unit test for a controller, and mock a tag lib?
As it stands, I have a User controller. Many of the actions use the
g.message(code: 'something.something')
call to set a message on the page.
#TestFor(UserController)
#Mock(User)
#TestMixin(GroovyPageUnitTestMixin)
class UserControllerSpec extends Specification
{
UserService userServiceMock = Mock(UserService)
def setup()
{
controller.userService = userServiceMock
}
def cleanup()
{
}
void "test manageDevice"()
{
given:
def g = mockTagLib(FormTagLib)
when:
controller.manageDevice()
then:
model.pageTitle == 'message.device'
}
With that code I'm trying to hit the controller action but because of the g.message, it's failing with an error saying that it can't set a value to null. Pretty much because it doesn't see the "g.message"
I'm a little unsure if my unit test needs written differently, or if I'm just missing something.
Any help would be great!
EDIT:
Some updates using messageSource:
void "test manageDevice"()
{
given:
messageSource.addMessage 'user.devices', request.locale, 'Manage Devices'
when:
controller.manageDevices()
then:
assertEquals model.pageTitle == 'user.devices', controller.flash.message
}
It seems to still be complaining because it doesn't have context of the "g" namespace on the controller. I'll note as well, I don't have context of 'addMessage' from messageSource. Not sure why, it should be there.
In the same controller, and many others, the only taglib we use in the controller scope is 'g' for the 'g.message' set in each action. The only other call that's being done, is one using the 'g' for a call like 'g.fixRedisIssue'
You can make a unit test for a controller with a mocked TagLib by including the necessary TagLib classes in the value of the grails.test.mixin.Mock annotation on the Spec class. For example, if you have the following TagLib:
(based on code in Grails documentation)
package org.grails.samples
class SimpleTagLib {
static namespace = 'g'
def hello = { attrs, body ->
out << "Hello ${attrs.name ?: 'World'}"
}
}
And you have the following controller:
package org.grails.samples
class SimpleController {
def flashHello() {
flash.message = g.hello()
}
}
You could test it with the following specification:
package org.grails.samples
import grails.test.mixin.Mock
import grails.test.mixin.TestFor
import spock.lang.Specification
#TestFor(SimpleController)
#Mock(SimpleTagLib)
class SimpleControllerSpec extends Specification {
void 'test flashHello'() {
when:
controller.flashHello()
then:
flash.message == 'Hello World'
}
}
If you have multiple classes to mock, you can specify them in an array argument to #Mock. So you can add any necessary tag libraries to UserControllerSpec by changing #Mock(User) to something like #Mock([User, FormTagLib, YourFixRedisIssueTagLib]). However, I'd be surprised if you actually did need to add FormTagLib because in my Grails testing it seems to be included by default.
As some further advice, some of the code you posted doesn't make any sense. Specifically:
assertEquals model.pageTitle == 'user.devices', controller.flash.message
Decide specifically what you want to test for and focus on writing the simplest code possible for that.
Also, based on your description of what's happening, I think that you're getting thrown off because your IDE is not properly integrated with Grails to see the g context.
Instead of returning the message text to the view from the controller, why not return the message code instead? For example, if you have something like this in your view:
<p>${flash.message}</p>
You can replace it with this:
<p><g:message code="${flash.code}" /></p>
And then set the code in the controller:
flash.code = "user.devices"
Then you'll be able to test the controller methods painlessly :)

initializeError while Unit-testing Mock objects with Mockito in Xtend

I'm trying to run this unit test, I'm currently using Xtend over Java in order to read the code easily.
The test consist on an admin who must verify a user in order to add it or not to the current repository. I want to make that admin a mock object in order to verify if the user has send correctly the method "generateProfile", which do the following
class User{
#Accessors
repositoryAdministrator admin
def generateProfile{
admin.add(this)
}
the method add is the following:
class repositoryAdministrator{
#Accessors List<User> objects
#Accessors List<User> usersToValidate
def add(User user){
usersToValidate.add(user)
}
This is the test i want to run using the lib Mockito
#RunWith(MockitoJUnitRunner)
class MockitoTests{
val lala = new User()
#Mock
repositoryAdministrator fakeAdmin
#Before
def void init(){
MockitoAnnotations.initMocks(this)
}
#Test
def validationTest(){
lala.admin = fakeAdmin
lala.generateProfile
Mockito.verify(fakeAdmin).add(lala)
}
}
I have imported correctly the libraries, I'm working on an Eclipse IDE, and when i run the test I keep on getting initializationError.
How do I properly initialize a mock object using Mockito? Sorry for my English
I've already found the problem, test using Xtend are not necessary to define the return type... Until you implement Mockito dependencies on a Maven proyect. There was a problem in the add method of the admin, it return a bool variable and it should return void, the way to fix it was:
#RunWith(MockitoJUnitRunner)
class MockitoTests{
val lala = new User()
#Mock
repositoryAdministrator fakeAdmin
#Before
def void init(){
MockitoAnnotations.initMocks(this)
}
#Test
def void validationTest(){
lala.admin = fakeAdmin
lala.generateProfile
Mockito.verify(fakeAdmin).add(lala)
}
}
I only define the return type in the test, which should be in this case void

Issue testing Laravel Controller with Mockery | trying to get property of non-object

I'm very new to testing controllers and I'm running into a problem with a method(). I believe I'm either missing something in my test or my Controller / Repository is designed incorrectly.
The application I'm writing is basically one of those secure "one time" tools. Where you create a note, the system provides you with a URL, once that url is retrieved the note is deleted. I actually have the application written but I am going back to write tests for practice (I know that's backwards).
My Controller:
use OneTimeNote\Repositories\NoteRepositoryInterface as Note;
class NoteController extends \Controller {
protected $note;
public function __construct(Note $note)
{
$this->note = $note;
}
public function getNote($url_id, $key)
{
$note = $this->note->find($url_id, $key);
if (!$note) {
return \Response::json(array('message' => 'Note not found'), 404);
}
$this->note->delete($note->id);
return \Response::json($note);
}
...
I've injected my Note interface in to my controller and all is well.
My Test
use \Mockery as M;
class OneTimeNoteTest extends TestCase {
public function setUp()
{
parent::setUp();
$this->mock = $this->mock('OneTimeNote\Repositories\EloquentNoteRepository');
}
public function mock($class)
{
$mock = M::mock($class);
$this->app->instance($class, $mock);
return $mock;
}
public function testShouldReturnNoteObj()
{
// Should Return Note
$this->mock->shouldReceive('find')->once()->andReturn('test');
$note = $this->call('GET', '/note/1234567890abcdefg/1234567890abcdefg');
$this->assertEquals('test', $note->getContent());
}
}
...
The error I'm getting
1) OneTimeNoteTest::testShouldReturnNoteObj
ErrorException: Trying to get property of non-object
/Users/andrew/laravel/app/OneTimeNote/Controllers/NoteController.php:24
Line 24 is in reference to this line found in my controller:
$this->note->delete($note->id);
Basically my abstracted repository method delete() obviously can't find $note->id because it really doesn't exist in the testing environment. Should I create a Note within the test and try to actually deleting it? Or would that be something that should be a model test? As you can see I need help, thanks!
----- Update -----
I tried to stub the repository to return a Note object as Dave Marshall mentioned in his answer, however I'm now receiving another error.
1) OneTimeNoteTest::testShouldReturnNoteObj
BadMethodCallException: Method Mockery_0_OneTimeNote_Repositories_EloquentNoteRepository::delete() does not exist on this mock object
I do have a delete() method in my repository and I know it's working when I test my route in the browser.
public function delete($id)
{
Note::find($id)->delete();
}
You are stubbing the note repository to return a string, PHP is then trying to retrieve the id attribute of a string, hence the error.
You should stub the repository to return a Note object, something like:
$this->mock->shouldReceive('find')->once()->andReturn(new Note());
Building upon Dave's answer, I was able to figure out what my problem is. I wasn't mocking the delete() method. I didn't understand the need to mock each individual method in my controller that would be called.
I just added this line:
$mock->shouldReceive('delete')->once()->andReturnNull();
Since my delete method is just deleting the note after it is found, I went ahead and mocked it but set it to return null.

Grails 2.1 Unit Testing Command Object mockForConstraintsTests not working?

I have used manually written as well as Grails generated Unit tests for this command object:
package myapp
#grails.validation.Validateable
class SearchCommand {
String basisBuild
String buildToSearch
static constraints = {
basisBuild(blank: false)
}
}
After having my hand written unit test fail I used Grails:
create-unit-test myapp.SearchCommand
I filled in the Unit Test, and made an assertion that should pass per documentation on mocked constraints:
package myapp
import static org.junit.Assert.*
import grails.test.mixin.*
import grails.test.mixin.support.*
import org.junit.*
#TestMixin(GrailsUnitTestMixin)
class SearchCommandTests {
void setUp() {
mockForConstraintsTests(SearchCommand)
}
void tearDown() {
// Tear down logic here
}
void testSomething() {
SearchCommand commandUnderTest = new SearchCommand()
commandUnderTest.validate(basisBuild: "")
assertEquals "blank", commandUnderTest.errors['basisBuild']
}
}
Why am I getting this failure?
grails> test-app
| Running 9 unit tests... 9 of 9
| Failure: testSomething(com.siemens.soarian.sf.gap.SearchCommandTests)
| java.lang.AssertionError: expected:<blank> but was:<null>
at org.junit.Assert.fail(Assert.java:93)
I believe I found the grails supported way to unit test Command objects in grails 2.0. You need to use mockCommandObject provided by the ControllerUnitTestMixin.
Credit to Erik
http://www.jworks.nl/2012/04/12/testing-command-objects-in-grails-2-0/
EDIT
Using validate() appropriately and mockForConstraintsTest should work if the patch mentioned in the existing Grails bug is in place (Thanks to #codelark for bringing that up). In order to test the command object from a Web App standpoint (using controller) the below information would be helpful.
Test Command Object Using Controller action:-
A command object is only deemed as such when it is used as a parameter in one of the action method inside a controller. Refer Command Objects (Warning NOTE).
Use SearchCommand in an action method, you should be able to assertEquals.
Sample:
void testSomething() {
YourController controller = mockController(YourController) //Or instantiate
SearchCommand commandUnderTest = new SearchCommand ()
//Note the usage here. validate() does not take parameters
commandUnderTest.basisBuild = ''
commandUnderTest.validate()
//Call your action
controller.searchCommandAction(commandUnderTest)
assert response.text == 'Returned'
assertEquals "blank", commandUnderTest.errors['basisBuild']
}
YourController's action:-
def searchCommandAction(SearchCommand sc){
render "Returned"
}
Note:
With out the patch from the grails bug we see the below error in #Grails 2.1.4, 2.2.0 & 2.2.1
I get an error when I only correct the validation and use mockForConstraintTests without using controller action:
You are using the validate method incorrectly. You never set the field on the class, so the field is null, not blank. Try changing your test as follows:
void testSomething() {
SearchCommand commandUnderTest = new SearchCommand()
commandUnderTest.basisBuild = ""
assertFalse commandUnderTest.validate()
assertEquals 'blank', commandUnderTest.errors['basisBuild']
}
Edit: There is also a grails bug when testing command classes that use the #Validatable annotation. There are some workarounds in the bug commentary.