I cannot figure out why my integration tests here are throwing exceptions.
Integration Test ##
package sample
import grails.test.mixin.*
import org.junit.*
/**
* See the API for {#link grails.test.mixin.web.ControllerUnitTestMixin} for usage instructions
*/
#TestFor(UserController)
class UserControllerTests extends GroovyTestCase {
User user
UserController uc
void setUp() {
//Save a User
user = new User(userName: "User1", firstName: "User1FN", lastName: "User1LN")
user.save()
//Set up UserController
uc = new UserController()
}
void tearDown() {
user.delete()
}
/**
* Test the UserController.handleLogin action.
*
* If the login succeeds, it will put the user object into the session.
*/
void testHandleLogin() {
//Setup controller paramaters
uc.params.userName = user.userName
//Call the action
uc.handleLogin()
//if the action functioned correctly, it put a user object into the session
def sessUser = uc.session.user
assert sessUser
assertEquals("Expected ids to match", user.id, sessUser.id)
//And the user was redirected to the Todo Page
assertTrue uc.response.redirectedUrl.startsWith("/todo")
}
/**
* Test the UserController.handleLogin action.
*
* If the login fails, it will redirect to login and set a flash message.
*
*/
void testHandleLoginInvalidUser() {
//Setup controller parameters
uc.params.userName = "Invalid_Name"
//Call the action
uc.handleLogin()
assertEquals "/user/login", uc.response.redirectedUrl
def message = uc.flash.message
assert message
assert message.startsWith("User not found")
}
/*
* Test the UserController.logout action
*
* If the logout action succeeds, it will remove the user object from the session.
*/
void testLogout (){
//Make a user logged into session
uc.session.user = user
//Call the action
uc.logout()
def sessUser = uc.session.user
assertNull ("Expected session user to be null", sessUser)
assertEquals "/user/login", uc.response.redirectedUrl
}
}
Console output for test
| Loading Grails 2.2.3
| Configuring classpath.
| Environment set to test.....
| Packaging Grails application.....
| Packaging Grails application.....
| Compiling 1 source files.
| Running 3 integration tests... 1 of 3
| Running 3 integration tests... 2 of 3
| Failure: testHandleLoginInvalidUser(sample.UserControllerTests)
| java.lang.NullPointerException: Cannot invoke method getBean() on null object
at grails.test.mixin.web.ControllerUnitTestMixin$_mockController_closure3.doCall(ControllerUnitTestMixin.groovy:304)
at grails.test.mixin.web.ControllerUnitTestMixin.mockController(ControllerUnitTestMixin.groovy:311)
| Failure: testHandleLoginInvalidUser(sample.UserControllerTests)
| java.lang.NullPointerException: Cannot invoke method delete() on null object
at sample.UserControllerTests.tearDown(UserControllerTests.groovy:25)
| Running 3 integration tests... 3 of 3
| Failure: testLogout(sample.UserControllerTests)
| java.lang.NullPointerException: Cannot invoke method getBean() on null object
at grails.test.mixin.web.ControllerUnitTestMixin$_mockController_closure3.doCall(ControllerUnitTestMixin.groovy:304)
at grails.test.mixin.web.ControllerUnitTestMixin.mockController(ControllerUnitTestMixin.groovy:311)
| Failure: testLogout(sample.UserControllerTests)
| java.lang.NullPointerException: Cannot invoke method delete() on null object
at sample.UserControllerTests.tearDown(UserControllerTests.groovy:25)
| Failure: sample.UserControllerTests
| java.lang.NullPointerException: Cannot invoke method isActive() on null object
at grails.test.mixin.support.GrailsUnitTestMixin.shutdownApplicationContext(GrailsUnitTestMixin.groovy:234)
| Completed 3 integration tests, 5 failed in 980ms
You're using unit test annotations in an integration test - that will cause lots of problems. In general when doing integration tests you extend GroovyTestCase if you want to use JUnit3-style tests, or nothing and use JUnit4 annotations, or use Spock and extend IntegrationSpec.
As for the NPEs, whether you're using proper unit tests or integration tests, you need to manage the controller's dependencies yourself since you explicitly create it with new and don't access it as a pre-wired Spring bean. But the integration test does support dependency injection, so just add fields in your test for whatever you need in the controller, and in setUp or in individual methods you can set those beans in the controller, e.g.
class UserControllerTests extends GroovyTestCase {
def grailsApplication
def someSpringBean
def someOtherSpringBean
private UserController uc = new UserController()
protected void setUp() {
super.setUp()
user = new User(userName: "User1", firstName: "User1FN", lastName: "User1LN").save()
//Set up UserController
uc.applicationContext = grailsApplication.mainContext
uc.someSpringBean = someSpringBean
uc.someOtherSpringBean = someOtherSpringBean
}
...
Also note that you don't need to cleanup your data in tearDown() - integration tests run in a transaction that's rolled back at the end of the test method.
Related
When do grails unit test with Spock, can't auto inject a service instance to domain.
Below is my code.
Service:
class HiService {
public HiService(){
println "Init HiService," + this.toString()
}
def sayHi(String name){
println "Hi, ${name}"
}
}
Domain:
class User {
public User(){
if (hiService == null){
println "hiService is null when new User(${name})"
}
}
String name
def hiService
def sayHi(){
println "Before use hiService " + hiService?.toString()
hiService.sayHi(name)
println "End use hiService" + hiService?.toString()
}
}
TestCase:
#TestFor(HiService)
#Mock([User])
class HiServiceTest extends Specification {
def "test sayHi"() {
given:
def item = new User( name: "kitty").save(validate: false)
when: "Use service method"
item.sayHi()
then : "expect something happen"
assertEquals(1, 1)
}
}
The following was console log:
--Output from test sayHi--
Init HiService,test.HiService#530f5e8e
hiService is null when new User(null)
Before use hiService null
| Failure: test sayHi(test.HiServiceTest)
| java.lang.NullPointerException: Cannot invoke method sayHi() on null object
at test.User.sayHi(User.groovy:17)
at test.HiServiceTest.test sayHi(HiServiceTest.groovy:20)
The service initialized, but can't inject to domain. But when run app directly, service will auto-inject to domain
If you wan't autowiring it needs to be an integration test. If using Grails 3 then annotate with #Integration, if grails 2 then extend IntegrationSpec.
See: http://docs.grails.org/latest/guide/testing.html#integrationTesting
#Mock([User, HiService])
class HiServiceTest extends Specification {
def "test sayHi"() {
// ....
}
}
Since you are writing unit tests, your service will not be autowired. Also as you are unit testing the User class object, you should write the test in UserSpec (instead of UserServceTest; Suffixing Spec is the convention in Spock). Now you can mock the HiService instead like this:
class UserSpec extends Specification {
def "User is able to say hi"() {
given:
User user = new User(name: 'bla bla')
and: "Mock the user service"
def hiService = Mock(HiService)
user.hiService = hiService
when:
user.sayHi()
then:
1 * sayHiService.sayHi(user.name)
}
}
With grails 2.5.1, I only created a service with a simple property initialized through a constructor (#PostConstruct also)
Any unit test that read this property through a service method get a NullPointerException
Here's how to reproduce:
grails> create-app acme
grails> create-service DataService
acme/DataService.groovy
#Transactional
class DataService {
def data = null;
#PostConstruct
def setup() {
if (data == null) {
println("Initializing the service ...")
data = "<DATA>"
}
}
def serviceMethod() {
setup()
}
def getDataLength() {
println("Getting data length ...")
return data.length(); // Line 26
}
}
acme/DataServiceSpec.groovy
#TestFor(DataService)
class DataServiceSpec extends Specification {
def setup() {
}
def cleanup() {
}
void "test data"() {
given:
int datalen = service.getDataLength()
expect:
datalen > 0
}
}
Running the test ....
grails> test-app -unit -echoOut
The output ...
|Running 2 unit tests...
|Running 2 unit tests... 1 of 2
--Output from test data--
Initializing the service ...
Getting data length ...
Failure: |
test data(acme.DataServiceSpec)
|
java.lang.NullPointerException: Cannot invoke method length() on null object
at acme.DataService.getDataLength(DataService.groovy:26)
at acme.DataServiceSpec.test data(DataServiceSpec.groovy:20)
|Completed 1 unit test, 1 failed in 0m 2s
.Tests FAILED
The log shows the property initialization step and the NullPointerException for the property.
The questions are:
Why the 'data' property becomes null ?
There's a service lifecycle setting the property to null ?
Is the running test using a different DataService instance from the initialized instance ?
Have you seen Grails unit test fails on #PostConstruct?
In unit test and with #TestFor annotation #PostConstruct is not called. You can create construct for test or call setup method.
I am trying to write unit for one of my grails service method, the unit test is like
#Shared instance1, instance2, instance3
class testClass extends Specification {
def setup() {
// initaiting some value here
instance1 = new Initate(one:"one")
instance2 = new Initate(one:"one")
instance3 = new Initate(one:"one")
}
def "testmethodcall"() {
//testsetup()
when:
def result = someMethod(value1) // Here is the error
then:
result==value2
where:
value | value1 | value2
instance1 | instance2 | instance3
}
}
for some reason we planned to move the codes inside this setup method to another method and plan to call where it is required, like this
#Shared instance1, instance2, instance3
class testClass {
def testsetup() {
// initialize the code what we initialize in setup
// initaiting some value here
instance1 = new Initate(one:"one")
instance2 = new Initate(one:"one")
instance3 = new Initate(one:"one")
}
def "testmethodcall"() {
testsetup()
when:
def result = someMethod(value1) // Here is the error
then:
result==value2
where:
value | value1 | value2
instance1 | instance2 | instance3
}
}
As of now it is fine, the method call is working fine, even variable were been initialized, but when I trying to use the data column values it is returning null values, but when I change to setup() method I getting the real values. Can someone explain me and How can I able to change the setUp method as normal method
There are few things to note down:-
setup() - is used to run with every test case
setupSpec() - is used to run once for a test class / unit
shared - is visible throughout.And hence must be initialized once, Hence must be within worked upon in setupSpec and not within setup.
cleanup() - is run with every test case
cleanupSpec - is run in last, after all the test cases has run.
Only #Shared and static variables can be accessed from within a where: block.
Seperate inputs and outputs expected with double pipes ||
Apart from these point, in your code testmethodcall method is having syntax errors.
no comparison is done
expect: is missing
why def value is there ,whereas we use data tables variables are declared automatically
Also, value2 is used no where
Also, you stated that you are able to run test cases successfully with above code when setup() method is there, it's just passing silently without failing as both the value and value1 are null.
Below is a modified version of your code
def "test methodcall"(){
setup()
expect:
value == someMethod(value1) // Here is the error
println '----instance1----'+value
println '----instance2----'+value1
println '----instance1----'+instance1
println '----instance2----'+instance2
// instance1 == instance2
where:
value | value1
instance1 | instance2
}
Now see the println output and you will understand the issue.Hope this would get you answer.
I am doing Grails tutorials on IBM(here) but I am a quite disappointed by an integration test.
To sum up : I call a method who render a JSON object according to the ID (iata).
My domain is :
class Airport {
String name
String iata
}
My controller is :
class AirportController {
// In order to enable scaffolding
def scaffold = Airport
def iata = {
def iata = params.id?.toUpperCase() ?: "NO IATA"
def airport = Airport.findByIata(iata)
if (!airport) {
airport = new Airport(iata: iata, name: "Not found")
}
render airport as JSON
}
}
When I do :
http://localhost:8080/trip-planner/airport/iata/foo (in order to retreive null value) or
http://localhost:8080/trip-planner/airport/iata/DEN (for DENVER), the method works fine !
The issue is my Integration tests :
class AirportControllerTests extends GroovyTestCase {
void testWithGoodIata(){
def controller = new AirportController()
controller.metaClass.getParams = { ->
return ["id":"den"]
}
controller.iata()
def response = controller.response.contentAsString
assertTrue response.contains("Denver")
}
void testWithWrongIata() {
def controller = new AirportController()
controller.metaClass.getParams = { ->
return ["id":"foo"]
}
controller.iata()
def response = controller.response.contentAsString
assertTrue response.contains("\"name\":\"Not found\"")
}
}
The problem is:
Whenever I run the tests (by running : grails test-app -integration trip.planner.AirportControllerTests), I will always obtain a good behavior in the first test and a groovy.lang.MissingMethodException in the second test. (even if I switch the two : the second test always fail)
If I run them separately , it works.
The exception occurred at this line (in the controller) : def airport = Airport.findByIata(iata)
Is that someting to do with "transactional" ? Any help would be great :)
P.S : I am using Grails 2.2.1
The exception stacktrace :
groovy.lang.MissingMethodException: No signature of method: trip.planner.Airport.methodMissing() is applicable for argument types: () values: []
at trip.planner.AirportController$_closure4.doCall(AirportController.groovy:39)
at trip.planner.AirportControllerTests.testWithWrongIata(AirportControllerTests.groovy:25)
I suspect the metaclass changes you're making in one test are somehow leaking through to the other. But you don't need to (and shouldn't) manipulate the metaclass in an integration test, just say
def controller = new AirportController()
controller.params.id = "den"
You only need to do mocking for unit tests.
Bear in mind that the tutorial you're looking at was written way back in 2008 (in the Grails 1.0.x days), and Grails has moved on a very long way since then, with some components (including testing) having been through one or more complete rewrites.
I have a class like
class Account {
BigDecimal balance = 0
SortedSet transactions
AccountOwner owner
static constraints = {
}
static hasMany = [transactions:Transaction]
}
when I try to query the Account like
def account = Account.findByOwner(user)
I get this error
| Failure: testSave(br.com.fisgo.financial.AccountControllerTests)
| org.springframework.dao.InvalidDataAccessResourceUsageException: Cannot query [br.com.fisgo.financial.Account] on non-existent property: owner
at org.grails.datastore.mapping.simple.query.SimpleMapQuery.getValidProperty(SimpleMapQuery.groovy:706)
at org.grails.datastore.mapping.simple.query.SimpleMapQuery.executeSubQueryInternal(SimpleMapQuery.groovy:644)
at org.grails.datastore.mapping.simple.query.SimpleMapQuery.executeSubQuery(SimpleMapQuery.groovy:630)
at org.grails.datastore.mapping.simple.query.SimpleMapQuery.executeQuery(SimpleMapQuery.groovy:63)
at org.grails.datastore.mapping.query.Query.list(Query.java:486)
at org.grails.datastore.gorm.finders.AbstractFindByFinder.invokeQuery(AbstractFindByFinder.java:34)
at org.grails.datastore.gorm.finders.AbstractFindByFinder$1.doInSession(AbstractFindByFinder.java:26)
at org.grails.datastore.mapping.core.DatastoreUtils.execute(DatastoreUtils.java:301)
at org.grails.datastore.gorm.finders.AbstractFinder.execute(AbstractFinder.java:40)
at org.grails.datastore.gorm.finders.AbstractFindByFinder.doInvokeInternal(AbstractFindByFinder.java:24)
at org.grails.datastore.gorm.finders.DynamicFinder.invoke(DynamicFinder.java:151)
at org.grails.datastore.gorm.finders.DynamicFinder.invoke(DynamicFinder.java:352)
at org.grails.datastore.gorm.GormStaticApi.methodMissing(GormStaticApi.groovy:108)
at br.com.fisgo.financial.AccountController.buyLead(AccountController.groovy:17)
at br.com.fisgo.financial.AccountControllerTests.testSave(AccountControllerTests.groovy:92)
| Completed 1 unit test, 1 failed in 5414ms
| Tests FAILED - view reports in target\test-reports
Using this interface
package br.com.fisgo.financial;
public interface AccountOwner {
}
I'm using mocked object for testing
Thanks
Since findBy* are handled by GORM and GORM doesn't handle interfaces I don't think it's going to work. Could you make AccountOwner an abstract class?