I would like to know if there is a way to use Django's test client to verify that the extra variables that are passed from a generic view are correct.
For example, given the code below, how would I write a test for list_year() to ensure that the template is receiving the current year from the view, or for purchase_yrs() to make sure the correct information is being passed from the model manager?
I can't seem to find a way to draw it out of the response.context attribute.
class PurchaseIndex(generic.ListView):
def get_queryset(self):
current_date = datetime.now().year
return Purchase.objects.filter(purchase_date__year=current_date).reverse()
def purchase_yrs(self):
return Purchase.purchase_years.purchase_years_list()
def list_year(self):
return datetime.now().year
Okay, I finally figured this one out. Pretty simple:
from django.test import TestCase
from views import PurchaseIndex
class IndexPageTest(TestCase):
def test_current_purchase_index_displays_year(self):
context = PurchaseIndex.list_year(self)
self.assertEqual(2015, context)
def test_current_shipments_included_in_current_purchase_index(self):
context = PurchaseIndex.purchase_yrs(self)
self.assertIn(2015, context)
self.assertIn(2014, context)
Related
When reviewing the source code of Django-allAuth.
class OAuth2Adapter(object):
...
def get_provider(self):
return providers.registry.by_id(self.provider_id, self.request)
Within class OAuth2Adapter, I did not see anywhere self.provider_id was given an value.
How can the attribute self.provider_id be used without initializing ?
I need a list of my domains, and I am getting them by using:
def domains = grailsApplication.getArtefacts("Domain")*.clazz
In each of my domains is a constructor, I want to do something like this:
def item = new domains[0](object)
item.save()
essentially making a generic save.
I figured out a way to invoke the constructor. I don't know if it is the best way to do it. Example code below:
//provides all Domain classes in your project in an ArrayList
//You can also use getArtefact("Domain", "classname")*.clazz to return a subset
def domains = grailsApplication.getArtefacts("Domain")*.clazz
def domain = domains[0].newInstance(object) //Whatever your constructor wants variable: "item" works as well
def domain.save()
Got this working thanks to http://mrhaki.blogspot.ca/2010/06/groovy-goodness-create-class-instance.html
I'm making a web app that stores reports of various types as domain objects, so I have a domain object HeadOfHousehold which contains name data, and references to other domain objects such as the reports, addresses, and any dependants. I am trying to build a list of recently viewed/created HeadOfHousehold objects. After multiple Google searches, and scouring the manual, it appeared that a service would be an appropriate solution. So I created ClientListService:
#Transactional
class ClientListService {
static scope = "session"
String message // right now I'll be happy to just see the same message across
// pages I can add a list and manipulate it later.
}
I thought I could then reference it in my various controllers, and it would persist Something like this:
def clientListService
def index(){
hasSearched = false
clientListService = new ClientListService(message: "Hello")
[errorMessage: params.errorMessage, clients:clientListService]
}
Which should be available in a later controller:
class HeadOfHouseHoldController {
def clientListService
def index() {
[customer: HeadOfHousehold.get(params.id), clients: clientListService]
}//...
However when I try to get the message, it appears as if the object is null.
From my index.gsp:
***************${clients?.message}********************
So I don't know if I am not defining session properly (I'm not doing anything special to do so), if I'm misunderstanding how the session scope works, or something else. I do see the proper message on the original page which has defined the object, however I don't see it on any subsequent pages.
Also, I'm not sure if this is the proper way to go about this; right now all I really need is the list of HeadOfHouseholds that I would need (so I can add to the list from other pages), however I can see possibly adding other logic and items into such a class.
I think you understood the session scope correctly. Each Spring bean with a session scope is bound to the HTTP session.
But your first controller listing does it all wrong. You are not supposed to instantiate the service class yourself. This is what Spring (Grails) does.
class FooController {
def clientListService // gets autowired by Grails/Spring
def index(){
hasSearched = false
clientListService.message = 'Hello' // only assign a String value to the service
[errorMessage: params.errorMessage, clients:clientListService]
}
}
This means you cannot not do something like
clientListService = new ClientListService(message: "Hello")
and expect your code to work. Hope this helps.
Using Grails 2.1.0
It seems that doing this from a controller:
render(view: "someView", model: [modelEntry: "hello"])
allows me to do this in a unit test for that controller:
controller.method()
assert model.modelEntry == "hello"
However, if I change the controller to do this:
render(template: "someTemplate", model: [modelEntry: "hello"])
Now the model instance in the test is an empty array. I've done quite a bit of searching about this, and most of the solutions seem to be for Grails 1, often involving the modelAndView object (which doesn't exist in my test) or renderArgs (ditto).
The only solution I've found is to manually override the views within the test, like this:
views['_someTemplate.gsp'] = '${modelEntry}'
and then making assertions about the string. But I dislike this solution because it:
requires the test knows the filename of the template
makes it difficult to test model entries that don't have good toString() methods
makes it difficult to make multiple assertions about related model entries.
Is there any way to more directly get at the entries in the model from a test case when the controller renders a template?
Digging a little bit in the code of the render method (org.codehaus.groovy.grails.web.metaclass.RenderDynamicMethod) I can see that the modelAndView is setted only when you render a view.
Rendering a template will return a null modelAndView indeed.
To inspect the model in this case I think you can use the Groovy metaClass. The idea is to intercept the original method, store the value and then call him.
Based on this question, I builded this (not tested, may need adjusts):
#TestFor(MyController)
class MyControllerTests
def templateModel
#Test
void inspectTemplateModel() {
def originalMethod = MyController.metaClass.getMetaMethod('render', [Map] as Class[])
controller.metaClass.render = { Map args ->
templateModel = args.model
originalMethod.invoke(delegate, args)
}
controller.method()
assert templateModel.modelEntry == 'foo'
}
I have a filter and the controllerName var getting controller target.
For example:
when user try to access /myApp/book/index, my filter is triggered and controllerName is equals book. How can I get a BookController instance?
Tks
EDIT:
I can get an Artefact using:
grailsApplication.getArtefactByLogicalPropertyName("Controller", "book")
But what I do with this artefact?
The controller will be registered as a spring bean. Just grab it by name:
applicationContext.getBean('mypackage.BookController') // or
def artefact = grailsApplication.getArtefactByLogicalPropertyName("Controller", "book")
applicationContext.getBean(artefact.clazz.name)
As Burt said, you probably don't want one controller instance inside your filter. This is a wrong way to solve your problem.
Grails Controllers as injected automagically by Spring Framework, and there is some black magic and procedures made when creating it. So, I can assure you this is not the way to solve this problem.
As you yourself described, you want to call your action, and I can imagine you're trying to reuse some code that resides in your action, maybe to generate some data in your database, or even to work with your HTTP session, am I right?
So, you can do two things to solve this kind of issue.
1) Just redirect your request flow to to your controller/action like this:
if (something) {
redirect controller: 'xpto', action: 'desired'
return false
}
2) Or you can get the logic inside your action (that is doing that dirty job you want to run), separate that logic inside one service, and reuse the service in both classes (action / service) this way:
MyService.groovy
class MyService {
def methodToReuse() {
(...)
}
}
MyController.groovy
class MyController {
def myService //auto-injected by the green elf
def myAction = {
myService.methodToReuse()
}
}
MyFilters.groovy
class MyFilters {
def myService //auto-injected by the red elf
(...)
myService.methodToReuse()
(...)
}
[]s,
You should be able to call newInstance on the artefact you've retrieved. newInstance works just like the constructor so you can provide any parameters you would to a normal constructor call.
So you can probably just do:
def bookController = grailsApplication.getArtefactByLogicalPropertyName("Controller", "book").newInstance()
Working code:
import org.codehaus.groovy.grails.web.context.ServletContextHolder
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes
import org.springframework.context.ApplicationContext
ApplicationContext applicationContext = (ApplicationContext) ServletContextHolder.getServletContext().getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
def grailsApplication
String nameController = "search"
def artefact = grailsApplication.getArtefactByLogicalPropertyName("Controller", nameController)
def controller = applicationContext.getBean(artefact.clazz.name)