Grails controller test making assertions about model when rendering a template? - grails

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'
}

Related

Grails - Pass data from view to controller

I'm trying to pass Param data from view to controller and I'm having trouble. Here's what I'm currently trying to do.
View:
<form action="${doStuffURL}" method='post' params="[keyId: ${mykey.id[i]}]"><g:actionSubmit value="doStuff"/></form>
Controller:
def myObjectService //inject service object
def doStuff = {
myObjectService.doStuff("${params.keyId}") //this blows up because it's value of NULL
myObjectService.doStuff(8) //this works fine
}
It gets to the method block because the error log says "no signature of method MyObject.doStuff() is applicable for argument types: values: [null]." Also, I'm able to see ${mykey.id[i]} data from another field, so the data is definitely in the view.
How can I get the controller to read the Param data?
Thanks!
err lots wrong here:
<form action="${doStuffURL}" method='post' params="[keyId: ${mykey.id[i]}]"><g:actionSubmit value="doStuff"/></form>
why not use:
<g:form name="something" controller="yourController" action="youraction">
As you can see above you are having to generate
form url (maybe you have your reasons)
Controller:
def doStuff = {
MyObject.doStuff("${params.keyId}")
}
Differences between action and methods in Grails controllers
So firstly why you should change controller but my actual concern here is
MyObject.doStuff
is MyObject.doStuff a static method ?
since that is only when a call like this would work. The link shows a static method. gets called here and it may confuse you due to it calling it via executor.registerSenderFault due to how it is generated working - expandability - for future classes that do same thing. this could have been EmailExecutor.registerSenderFault which is the full class in uppercase like you have declared.
surely it should be a service notice starting with lower case.
myObjectServive.doStuff(stuff)
If above is some real method in MyObject and is not a static method then you need to instantiate the class
MyObject myObject = new MyObject()
myObject.doStuff(stuff)
but in short this is why services exist it is all there to save you all the hassle since they just get injected.
I suggest you do some reading looking around
E2A
def doStuff = {
println "my params are $params "
//do you see keyId in there ?
//if so what is doStuff expecting as input a string ?:
// since if you do
println "${params.keyId.getClass()}"
//by default it will be a string you may need to change it from:
//myObjectService.doStuff("${params.keyId}")
myObjectService.doStuff(params.keyId as Long)
}
Personally I don't think it is any of the above edited comments, it still relates to how/what you are injecting. I have seen similar issues. I would suggest you create a brand new service and inject new service as a test and start again - not convinced you were injecting it correctly or if you are the service may be some abstract class rather than a normal service. Or.... you are making some form of similar mistake in the uppercase/lowercase declaration of the service name so you may have created:
MyObjectnameService and calling it using myObjectNameService difference in N in those or.... even worse you have created actual service as myObjectNameService with lowercase name.
test this all again using a much simpler naming convention and create a new service as a test
grails create service MyService
and try again using this service

Grails3 generate-all generates faulty create action code

When I use generate-all package.DomainObject, it generates a controller where create action is generated as:
def create() {
respond new DomainObject(params)
}
When I call the localhost:8080/DomainObject/create even without making any code change, it throws an exception:
groovy.lang.MissingPropertyException: No such property: controller for
class: package.DomainObject
It looks like introspection is failing for properties that params map has and DomainObject does not have. This is surprising because in the grails 2, introspection used to just ignore the non-matching properties and it also used to do necessary type conversions on fields as well (now if DomainObject has an int property, it will throw a type mismatch exception because params map passes it as String). This is really inconvenient. Did something change or I am missing something?
Using the map constructor and setting properties in bulk with a map in Grails is basically the same as in Groovy, but it has logic to exclude 'controller', 'action', and 'format' keys to keep controller code like this uncluttered. That broke in 3.x and has been reported in the issue tracker. It's not marked fixed but works correctly for me in a simple 3.0.4 test app.
As a temporary workaround you can copy the params map and remove values stored under those keys and use the 'fixed' map for the constructor:
def create() {
def fixedParams = ([:] + params) // copy
['controller', 'format', 'action'].each { fixedParams.remove it }
respond new Thing(fixedParams)
}

Django - Writing Tests: Unit testing extra variables passed to generic views

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)

Grails Session scope for service not working as expected

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.

TDD and MVC Model Binding

Let's say I have this unit test:
[Test]
public void LastNameShouldNotBeEmpty()
{
ExampleController controller = new ExampleController();
Person editedPerson = new Person { FirstName = "j", LastName = "" };
controller.EditPerson(editedPerson);
Assert.AreEqual(controller.ModelState.IsValid, false);
}
And this code:
public class ExampleController : Controller
{
public ActionResult EditPerson(int personId)
{
// Serve up a view, whatever
return View(Person.LoadPerson(personId));
}
[HttpPost]
public ActionResult EditPerson(Person person)
{
if (ModelState.IsValid)
{
// TODO - actually save the modified person, whatever
}
return View(person);
}
}
public class Person
{
public string FirstName { get; set; }
[Required] public string LastName { get; set; }
}
It's bothering me that if I TDD out a requirement that the LastName can't be empty, I can't satisfy the test using DataAnnotation attributes (the [Required] before the LastName declaration on Person) because when the controller's action method is invoked from a unit test, the MVC infrastructure hasn't gotten a chance to apply the validation it does during model binding.
(If I manually performed validation in the controller's EditPerson method, though, and added an error to the ModelState, that would be verifiable from a unit test.)
Am I missing something? I'd like to specify the validation behavior of my system using unit tests, but I'm not sure how to satisfy a unit test unless I abandon DataAnnotation attributes altogether and perform validation manually inside my controller's action methods.
I hope the intent of my question is clear; is there a way to force true model binding to execute (including its validation behavior, to test that I haven't forgotten important validation attributes) from an automated unit test?
Jeff
Here's one solution that I came up with. It requires that one line of code be added to the unit test, but I'm finding that it lets me not care whether validation is enforced via attributes for via custom code in the action method, which feels like the test is more in the spirit of specifying outcomes rather than implementation. It allows the test to pass as written even though the validation is coming from data annotations. Note that the new line is right above the invocation of the EditPerson action method:
[Test]
public void LastNameShouldNotBeEmpty()
{
FakeExampleController controller = new FakeExampleController();
Person editedPerson = new Person { FirstName = "j", LastName = "" };
// Performs the same attribute-based validation that model binding would perform
controller.ValidateModel(editedPerson);
controller.EditPerson(editedPerson);
Assert.AreEqual(false, controller.ModelState.IsValid);
Assert.AreEqual(true, controller.ModelState.Keys.Contains("LastName"));
Assert.AreEqual("Last name cannot be blank", controller.ModelState["LastName"].Errors[0].ErrorMessage);
}
ValidateModel is actually an extension method I created (the controller does have a ValidateModel method but it is protected so it can't be invoked from a unit test directly). It uses reflection to call the protected TryValidateModel() method on the controller, which will trigger the annotation-based validations as though the action method were truly being called through the MVC.NET infrastructure.
public static class Extensions
{
public static void ValidateModel<T>(this Controller controller, T modelObject)
{
if (controller.ControllerContext == null)
controller.ControllerContext = new ControllerContext();
Type type = controller.GetType();
MethodInfo tryValidateModelMethod =
type.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).Where(
mi => mi.Name == "TryValidateModel" && mi.GetParameters().Count() == 1).First();
tryValidateModelMethod.Invoke(controller, new object[] {modelObject});
}
}
It seems to work with minimal invasiveness, although there may be ramifications that I'm not aware of . . .
Jeff
I agree it's not a very satisfying situation. However, there are some simple workarounds:
Work around this problem by reflecting on the data entities and looking for the necessary validation attributes (that's what I am currently doing). It's much easier than it may sound.
Build your own validator that reflects the viewmodel parameter type and validates it. Use it to verify in your unit tests if the proper validation attributes are set. Assuming that your validation class is bug-free it should be equivalent to the validation algorithm in the ASP.NET MVC ModelBinder. I've written such a validator class for a different purpose and it's not much more difficult than the first option.
I personally believe you should have unit tests that test for the attributes themselves, outside of the scope of MVC. This should be part of your model tests, not your controller tests. You didn't write the MVC validation code, so don't try to test it! Just test the fact that your object has the right attributes you are expecting.
This is very rough, but you get the idea...
[Test]
public void LastNameShouldBeRequired()
{
var personType = typeof(Person);
var lastNamePropInfo = objType.GetProperty("LastName");
var requiredAttrs = lastNamePropInfo.GetCustomAttributes(typeof(RequiredAttribute), true).OfType<RequiredAttribute>();
Assert.IsTrue(requiredAttrs.Any());
}
Then in your MVC tests you just test the flow of a controller, not the validity of the data annotations. You can tell modelstate that it is invalid to test the flow of what happens if validation fails etc by adding an error manually, as you noted. Then it's a very controlled test of what your controller is responsible for, not what the framework is doing for you.
I don't like tests that check for the presence of attributes personally, it makes the tests act less like documentation and tightly coupled with my understanding of ASP.NET MVC (which may be wrong) and not tightly coupled with the business requirements (which I care about).
So for these kind of things, I end up writing integration tests, generating HTTP requests directly or via the browser with WatiN. Once you get this going you can write tests without the extra MVC abstraction, the tests document what you really care about being true. That said, such tests are slow.
I've also done something where my integration tests can make a backdoor request, which causes a test fixture to be loaded within the server process. This text fixture will temporarily override bindings in my IOC container... This reduces the setup of the integration tests, though they're only half-integration tests at that point.
I might, for instance, replace a controller with a mock controller that will verify the action method is called with the expected parameter. More usually I replace the site's data source with another data source that I've prepopulated.
We can make use of Validator helper class to do TDD with Model Validation. You can find a detailed blog about test driving model validation here.

Resources