Grails3 controller integration test case fail: No thread-bound request found - grails

With just simple following controller action spock integration-test. Here is my Test.
#Integration
#Rollback
class TestControllerSpec extends Specification {
def setup() {
}
def cleanup() {
}
void "test something"() {
setup:
def c = new TestController()
c.index()
expect:
c.response.contentType !=null
}
}
getting following Exception
java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
at grails.web.api.WebAttributes$Trait$Helper.currentRequestAttributes(WebAttributes.groovy:45)
at grails.web.api.ServletAttributes$Trait$Helper.getRequest(ServletAttributes.groovy:42)

I've been doing this and it seems to work fine:
Add field:
#Autowired
WebApplicationContext ctx
In setup():
GrailsWebMockUtil.bindMockWebRequest(ctx)
In cleanup():
RequestContextHolder.resetRequestAttributes()

Unfortunately it might be a limitation in Grails 3, that you cannot use integration tests to test controllers.
To integration test controllers it is recommended you use
create-functional-test command to create a Geb functional test.
Source from Grails documentation
This seems to be a major change of direction from previous versions of grails. If you really need to test a controller in an integration test, you could try doing this:
NOTE: I realize this might be a bad practice, and it goes against Grails documentation, but sometimes you also need to test things more programmatically, where unit tests aren't sufficient, and are Geb tests aren't granular enough.
#TestFor(TestController) // This will provide a mocked "controller" reference
#Integration
#Rollback
class TestControllerSpec extends Specification {
// If TestController uses any services, have them autowired into this test
#Autowired
SomeService someService
def setupSpec() {
// Now connect those services to the controller
controller.someService = someService
}
void "test something"() {
when:
controller.index()
then:
response.contentType != null
}
}
WARNING: After some additional work with this format I did find a problem. Using #TestFor will call Holders.clear() when it is complete, which means that there will not be a grailsApplication object in Holders. This will cause problems if you have any integration tests that run after one that uses the approach above. After much digging, it doesn't look like there is an easy (or even hard) way of making this work, which is possibly why it is not supported in Grails 3. That being said, one option is to mark other integration tests with #TestFor, so that the Holders class will be properly populated. Is this a hack? Yes it is! You will need to decide if it is worth the effort of adding this overhead to all tests. In my case it was only one other integration test that needed this (as it is a small application), but if it was more than that I would not use this approach.

Related

Why is sessionFactory not injected for unit test using Grails 3.2?

I'm running into an issue where a unit test that extends HibernateSpec is failing due to the sessionFactory not being injected into the service under test. Whenever a method on the sessionFactory is called during the test, I get a NullPointerException (e.g. java.lang.NullPointerException: Cannot invoke method getClassMetadata() on null object) and the test subsequently fails.
I'm using Grails 3.2.4 and Hibernate 5.
This was working when the test used #TestMixin(HibernateTestMixin), but it looks like with some updates, the mixin is deprecated and suggests using HibernateSpec instead.
Here's a snippet from my test:
class TestDatabaseServiceSpec extends HibernateSpec {
def setup() {
}
def cleanup() {
}
void "test method"() {
when:
service.method()
then:
true
}
}
And here is a snippet from the service under test:
void method() {
...
TABLE_NAMES.add(sessionFactory.getClassMetadata('MyDomain').tableName)
...
}
I have tried to set service.sessionFactory in setup method as well as setupSpec method with the sessionFactory available in the test, but that did not help unfortunately. I have thought about using an integration test, but I would really like to see if I can continue to have this unit test work as it was before. Does anyone know if I am I doing something incorrectly or if there is a workaround/solution for this?
Thank you!
Viewing source code of HibernateSpec could give some hint. Could be there is no getSessionFactory() method in Hibernate 5 grails plugin that you are using.
I am using org.grails.plugins:hibernate5:6.0.6 with grails 3.2.5 and sessionFactory is not null for me in unit tests.
I understand that it is not always possible to upgrade, but it could give an idea what is the source of the problem.
Update:
service.sessionFactory = sessionFactory
also must be called in test setup.
What worked for me was to add the annotation #FreshRuntime to the spec and to add a doWithSpring closure to the test that sets up the sessionFactory for me (i.e. sessionFactory(InstanceFactoryBean, sessionFactory, SessionFactory)). Also, I made sure to add both service.sessionFactory = sessionFactory and service.transactionManager = transactionManager to the setupSpec method. Finally, I needed to override getDomainClasses() to work with all of the domain objects I wanted to in the test. Thanks for all the help!
The Test Mixins have been deprecated for the new traits since 3.2.3.
https://docs.grails.org/latest/guide/testing.html
Since Grails 3.3, the Grails Testing Support Framework is used for all
unit tests. This support provides a set of traits.
However, I still find myself using HibernateSpec as I can get multiple Domain classes from that, and the ServiceUnitTest for the service as well. The HibernateSpec has the needed sessionFactory.
E.g.
class MyServiceSpec extends HibernateSpec implements ServiceUnitTest<MyService>{
List<Class> getDomainClasses() { [Tag, TagLink, TagLinkValue, TagValue, TestDomain] }
...
def setup() {
service.sessionFactory = sessionFactory
...
}
// tests here with all those domain classes available and the service
}
The DataTest allows for multiple domain classes, but not the service as well.
https://testing.grails.org/latest/guide/index.html#unitTestingDomainClasses
the service test allows for the service, but not multiple domain classes.
https://testing.grails.org/latest/guide/index.html#unitTestingServices
You could use both of them together, but neither one of them has the session factory (that I could see), I'm on Grails 4.0.1. I looked through Github code for the latest and still don't see it available. Plus, the DataTest doesn't a hibernate session, it uses a SimpleMap instead so you can't use hql with it, which I wanted.
My Service was using the statelessSession
def statelessSession = sessionFactory.openStatelessSession()
String queryString = "select DISTINCT(t.tagRef) from com.fmr.aps.taggable.TagLink t group by t.tagRef order by t.tagRef ASC"
Query query = statelessSession.createQuery(queryString)
query.setReadOnly(true);
ScrollableResults results = query.scroll(ScrollMode.FORWARD_ONLY);
So I needed a unit test trait or base class with it and I spent several hours trying to make the new traits give me what I wanted. Even I began subclassing the DataTest class to try and make it give me the sessionFactory, but eventually just went with the HibernateSpec and it's nice to be able to run that one test class with several Domains, the Service and the sessionFactory.

How to login before executing Geb tests

I've got REST webservice based on Grails. There's obligatory authentication before you can access any page except login.
I use Geb & Spock for integration tests, running through Gradle. My question is: how can I provide authentication before executing any test?
I have 2 ideas:
Run tests in order (log in at first, then run others). How? (must be triggering by command gradle integrationTest)
Execute JS script before every test which authenticate me. How to implement this in test?
What kind of authentication?
For Basic authentication have a look at:
HTTP Basic Authentication credentials passed in URL and encryption
so you could just add that to your base url.
If you have to go through the login process, you can use the setupSpec() and setup methods. The former gets executed before anything else happens, the latter before each individual test method.
You can use the #Stepwise notation to stop cookies from being deleted between test methods.
If you have different geb classes and want all of them to be prepended with your setup or setupSpec method, use the extends notation:
class BaseClass extends GebSpec {
def setupSpec() {
<loginProcess>
}
}
#Stepwise
class ASpec extends BaseClass {
testMethod() { }
}
#Stepwise
class BSpec extends BaseClass {
testMethod() { }
}
note that this will execute the login process one extra time because it happens in the BaseClass as well.
I got around this with an if (this.getClass().getPackage().getName() != "gebMethods") block but I guess there might be a more elegant solution.
If it's a web service I'd not use Geb for testing it. Geb is a tool for driving browsers and REST web services are best tested using http clients, like REST-assured.
I would make authentication configurable in the app and disable it for most tests apart from ones that explicitly test authentication.

GRAILS 2.4.4: how to run unit tests only, or how to fix the generated integration tests?

With grails 2.4, when you create a controller for example, it creates a unit test class such as:
#TestFor(SessionService)
class SessionServiceSpec extends Specification {
def setup() {
}
def cleanup() {
}
void "test something"() {
}
}
so you create 20 controllers, and get 20 test classes.
When you try to run grails test-app, it fails with the following error:
java.lang.Exception:
No tests found matching grails test target pattern filter from org.junit.runner.Request
It never gets to run our integration tests.
So tests Grails creates for you do not work out of the box.
We could delete all the created test specs classes, but then we kind of want them there ready for if we want to write them.
is there a way to run our integration tests, without running the unit tests (which are broken) or:
is there a way to fix the test so they don't fall over? Someone posted add a valid when/then, but we don't know what to put in this, as what we have tried still generates the same exceptions.
It's a shame Grails doesn't tell you which tests or class throws this exception.
This looks like a weird argument to me. When you create a controller, unit spec is created for it by default based on a template which can be easily tailored according to your need.
grails install-templates
does it for you. Now to answer your questions:
Yes there is a way to only run integration tests.
grails test-app integration:
After using install-templates modify the Controller.groovy template under src\templates\testing to something
like:
#artifact.package#import grails.test.mixin.TestFor
import spock.lang.Specification
/**
* See the API for {#link grails.test.mixin.web.ControllerUnitTestMixin}
* for usage instructions
*/
#TestFor(#artifact.testclass#)
class #artifact.name# extends Specification {
def setup() {
}
def cleanup() {
}
void "test something"() {
expect:
1 == 1
}
}
This will not fail your test when run but it defeats the whole purpose of
writing failing test -> modify code -> fixing test approach
of TDD. I would rather implement a failing test and then write minimal logic in controller to pass the test.

How to call a Service in a Grails integration test

This thread describes how to call a Service in a view: How do I get an instance of a Grails service programmatically?
This describes how to call it in a Servlet: How do I get an instance of a Grails service programmatically?
This one says how to call it in a Controller: How to dynamically select a service in Grails
I need to get a handle to my service in an integration test. Is this possible?
If its an integration test, you have access to the entire runtime so just inject it like you do normally.
def someService
Have a look at Testing Controllers with Service.
Gist:
You have to initialize the service (spring bean) to controller in the test.
class FilmStarsTests extends GroovyTestCase {
def popularityService
void testInjectedServiceInController () {
def fsc = new FilmStarsController()
fsc.popularityService = popularityService
fsc.update()
}
}
Services are autowired in integration tests as in controllers.

How do I use executeQuery in grails testcase?

How do I use executeQuery in grails testcase?
A error : groovy.lang.MissingMethodException: No signature of method ***.executeQuery() is applicable for argument types: () values: []
I have already called mockDomain.
By the way, it is in unit test.
Thanks!
There's no support for HQL queries in unit tests yet, but we're working on it. But you shouldn't be doing persistence tests with mocks. Persistence tests should be done against a database in an integration test.
I usually move HQL queries to the domain class as static query methods. This way they're easy to mock in a unit test of a controller, service, etc. and then I test the method as part of the domain class integration test.
For example I'd have
class User {
String username
String password
...
static List findAllUsersBlahBlah(String foo, boolean bar) {
executeQuery('from User u where ...')
}
}
Then in a unit test I can mock that method with fake data since I don't care about persistence in a controller unit test - I know that it's properly tested in the correct place and I want to focus on the class under test, not its collaborators:
def users = [new User(...), new User(...)]
User.metaClass.static.findAllUsersBlahBlah = { String foo, boolean bar -> users }
We have successfully mocked executeQuery with Grails 2.0 in our project
#TestFor(BookController)
#TestMixin([DomainClassUnitTestMixin,ServiceUnitTestMixin])
#ConfineMetaClassChanges([Book])
class BookControllerSpec extends Specification{
mockDomain(Book)
Book.metaClass.static.executeQuery = {a,b,c-> return [Book]}
In Grails 2.5.4 you can use GroovyMock for mocking static methods implemented in Java:
GroovyMock(Book, global: true)
I just tested - it works also for _.executeQuery()

Resources