I'm trying to write a unit test for a simple controller that takes GSON as input and writes to DB.
GSON because, the input has nested objects.
Unit testing works if i use JSON, but JSON has its limitations with nested objects.
Domain Class:
class Artist {
String guid
String name
static constraints = {
name nullable: true
}
static mapping = {
guid blank:false, unique: true
}
static hasMany = [albums: Albums]
/*static marshalling={ deep 'albums' }*/
}
Controller implementation:
def create() {
def artist = new Artist(request.GSON)
if (!artist.save(flush: true)) {
artist.errors.each {
log.error("Error while creating Artist " + it)
}
render status: 500, layout: null
return
}
response.status = 201
render artist as GSON
}
Unit test:
#TestMixin(GsonUnitTestMixin)
#TestFor(ArtistController)
#Mock(Artist)
class ArtistControllerTests {
void testCreate() {
request.GSON = "{guid:123,"+
"name: 'Akon',"+
"albums: ["+
"{guid:1,"+
"name:'album 1'"+
"}]}"
controller.create()
assert response.status == 201
}
}
Exception:
Cannot get property 'manyToOne' on null object at
def artist = new Artist(request.GSON) in the controller
Any help will be greatly appreciated..
Related
I have the two domain clases:
class Persona {
String nombre
String apellidos
static hasMany = [pertenencias: Pertenencia]
static constraints = {
}
static mapping = {
pertenencias cascade: "all-delete-orphan"
}
}
class Pertenencia {
String nombre
static belongsTo = [persona:Persona]
static constraints = {
}
}
The service:
class MembresiaService {
#Transactional
def saveAll() {
def p = new Persona(nombre: 'carlos', apellidos: 'gm')
p.addToPertenencias(nombre: 'auto')
p.addToPertenencias(nombre: 'computadora')
p.addToPertenencias(nombre: 'casa')
p.save()
}
#Transactional
def deletePertenencias() {
def p = Persona.get(1)
p.pertenencias?.clear()
}
}
And the controller:
class TestController {
def membresiaService
def index() {}
def saveAll() {
membresiaService.saveAll()
redirect(action: "index")
}
def deletePertenencias() {
membresiaService.deletePertenencias()
redirect(action: "index")
}
}
When I execute saveAll() method from controller it saves the data in the database, when I execute deletePertenencias() from controller it deletes the "pertenecias" collection of Persona from the database (as expected).
I have installed the Grails console plugin , first time I execute the lines of saveAll() service method in the console, the result is the "persona" and its "pertenencias" in database. Then I execute the lines of deletePertenencias() service method in console but it doesn't delete the data of database and the "persona" object mantains the "pertenencias" (as if I had not run deletePertenencias() code).
Anyone kwnow why the code executed from console gives unexpected results?
I expect the result was the same from controller and console but the behaviour is different.
I have a User domain Class as follows:
package xyz
class User {
String login
String password
String firstName
String lastName
String address
String email
static hasMany = [
websites: AddWebsite
]
static constraints = {
login blank:false, size:5..15,matches:/[\S]+/, unique:true
password blank:false, size:5..15,matches:/[\S]+/
firstName blank:false
lastName blank:false
email email: true
}
}
and another AddWebsite domain class as follows:
package xyz
import xyz.User;
class AddWebsite {
String website
User user
static belongsTo = [user: User]
static constraints = { website url:true }
}
I am working with MongoDB at the backend. I need that every user who logs in can add websites but a user can't add same websites multiple times but same website can be added by different users. Whenever I try to add a website, it shows me "user cannot be null error".
My controller class for AddWebsite is as follows:
package xyz
class AddWebsiteController {
def index() {
}
def addWebsites() {
if(request.method == 'POST') {
def u = new AddWebsite()
u.properties[
'website'
] = params
if(u.website =="" ) {
u.errors.rejectValue("website", "addwebsite.website.empty")
return [addWebsite:u]
}
else if(u.save()) {
render view:'addWebsites', model:[message: "Successfully saved: \""+u.website+"\" Add more websites..."]
//redirect(controller: "AddWebsite", action: "websiteAdded")
}
else {
return [addWebsite:u]
}
}
}
def websiteAdded() {
}
def failed(){
}
}
Also, I am not sure how to test whether this one to many association. Please help.
Figured it out. I made this change in the controller so that the user won't be null anymore:
def addWebsites() {
if(request.method == 'POST') {
def w = new AddWebsite()
w.properties[
'website'
] = params
w.user = session["user"] //modified
if(w.website =="" ) {
w.errors.rejectValue("website", "addwebsite.website.empty")
return [addWebsite:w]
}
else if(w.save()) {
render view:'addWebsites', model:[message: "Successfully saved: \""+w.website+"\" Add more websites..."]
}
else {
return [addWebsite:w]
}
}
}
Given this mapping:
"/student/degree/$studentid/$degreecode"(controller:'student', action:'degree')
I am trying the tests below and both fail with AssertionFailedError: '...did not match any mapping' and both are valid URLs that do function. I can succeed with a test URL of just '/student/degree' which I think should fail as the parameters are required.
It seems the assertURL methods are not handling multiple parameters. Does this only work for 'id' and is it not possible to create tests for these URLs?
#TestFor(UrlMappings)
#Mock([StudentController])
class UrlMappingsTests {
void testUrlMappings() {
assertForwardUrlMapping("/student/degree/102345678/N", controller: "student", action: "degree") {
assertForwardUrlMapping("/student/degree/102345678/N", controller: "student", action: "degree") {
studentid = 102345678
degreecode = "N"
}
}
}
}
Tested with Grails 2.2.4, and it works for me, I just add a custom mappings class:
package test
class MyUrlMappings {
static mappings = {
"/test/show/$myId/$myVal"(controller: "test", action: "show")
}
}
import spock.lang.*
import test.MyUrlMappings
#TestFor(MyUrlMappings)
#Mock([TestController])
//I'm using Spock
class MyUrlMappingsSpec extends Specification {
void "mapping should consider myId and myVal"() {
expect:
assertForwardUrlMapping("/test/show/1/tst", controller: "test", action: "show") {
//all params considered as String
myId = '1'
myVal = 'tst'
}
}
}
Is there an easy way to deserialize a JSON string to a domain class with support of embedded association; belongsTo and hasMany
{
name: "Customer",
contact: {
name: "Contact"
}
}
class Customer {
name
Contact contact
}
class Contact {
String name
static belongsTo = [customer:Customer]
}
in my controller I would like to do the following
def save() {
def customer = new Customer(request.JSON)
customer.save();
}
Now i'm forced to do
def save() {
def contact = new Contact(request.JSON.contact);
def customer = new Customer(request.JSON);
customer.contact = contact;
customer.save();
}
Have you tried using JsonSlurper?
Example usage:
def slurper = new JsonSlurper()
def result = slurper.parseText('{"person":{"name":"Guillaume","age":33,"pets":["dog","cat"]}}')
assert result.person.name == "Guillaume"
assert result.person.age == 33
assert result.person.pets.size() == 2
assert result.person.pets[0] == "dog"
assert result.person.pets[1] == "cat"
Ref: http://groovy.codehaus.org/gapi/groovy/json/JsonSlurper.html
you can try this
Test test
def result = new JsonSlurper().parseTest('yourString')
test = result
Try this will work.
The first line of my action is tested with an extra integration test.
The second line is tested with an extra unit test for automapper mapping stuff
The third line is untested then.
The below unit test does it test the third line? because it just tests the return type.
This seems stupid or too trivial too me. What else should the method return????
There is no if/else logic inside this action. Therefore just testing for type == JsonNetResult seems superfluid for me. I would not even realize in my unit test if someone removes the success = true anonymous type.
Should I rather test the data of the JsonNetResult with QUnit?
I would be glad about some guidance and tips because the actions returning Json data drive me crazy... Its just fetch data from db and put it inside the JsonNetResult object.
Action
[HttpGet]
public ActionResult GetTemplateRootUnits(int templateId)
{
IEnumerable<Unit> units = _dataProvider.GetTemplateRootUnits(templateId);
IEnumerable<UnitTreeViewModel> unitTreeViewModels = Mapper.Map<IEnumerable<Unit>, IEnumerable<UnitTreeViewModel>>(units);
return new JsonNetResult(new { data = unitTreeViewModels, success = true });
}
Unit test
[Test]
public void GetTemplateRootUnits_TemplateExists_ReturnsJsonNetResult()
{
// ARRANGE
Mock<IUnitDataProvider> mock1 = new Mock<IUnitDataProvider>();
Mock<IMappingEngine> mock2 = new Mock<IMappingEngine>();
Mock<ControllerContext> mock3 = new Mock<ControllerContext>();
UnitController controller = new UnitController(mock1.Object, mock2.Object);
mock1.Setup(m => m.GetTemplateRootUnits(1)).Returns(new List<Unit>
{
new Unit{ UnitId = 1, Name = "Name1", HasChildren = false},
new Unit{ UnitId = 2, Name = "Name2", HasChildren = false},
new Unit{ UnitId = 3, Name = "Name3", HasChildren = false},
});
var unitTreeViewModels = new List<UnitTreeViewModel>
{
new UnitTreeViewModel { Id = 1, Name = "Name1", HasChildren = false},
new UnitTreeViewModel { Id = 2, Name = "Name2", HasChildren = false},
new UnitTreeViewModel { Id = 3, Name = "Name3", HasChildren = false},
};
// Thats the way AutoMapper is mocked
mock2.Setup(m => m.Map<IEnumerable<Unit>, IEnumerable<UnitTreeViewModel>>(It.IsAny<IEnumerable<Unit>>())).Returns(unitTreeViewModels);
// ACT
ActionResult result = controller.GetTemplateRootUnits(1);
// ASSERT - check that the dataProvider.GetTestplanRootUnits() was called
mock1.Verify(m => m.GetTemplateRootUnits(1));
// ASSERT
Assert.IsInstanceOfType(typeof(JsonNetResult), result);
}
JsonNetResult.cs
public class JsonNetResult : ContentResult
{
private readonly object _data;
public JsonNetResult(object data)
{
_data = data;
}
public override void ExecuteResult(ControllerContext context)
{
Content = JsonConvert.SerializeObject(_data);
ContentType = "application/json";
base.ExecuteResult(context);
}
public object Data { get { return _data; } }
}
You haven't shown what the JsonNetResult class is, but I will assume that it is some custom action result using JSON.NET as serializer instead of the default JavaScriptSerializer. I will also assume that this class exposes a public property of type object called Data holding the model that is to be serialized:
public class JsonNetResult: ActionResult
{
public JsonNetResult(object data)
{
Data = data;
}
public object Data { get; private set; }
...
}
So you could test the data inside the result:
// ASSERT
Assert.IsInstanceOfType(typeof(JsonNetResult), result);
var jsonResult = result as JsonNetResult;
var data = new RouteValueDictionary(jsonResult.Data);
Assert.IsTrue((bool)data["success"]);
Assert.IsInstanceOfType(data["data"], typeof(IEnumerable<UnitTreeViewModel>));
Assert.AreEqual(unitTreeViewModels, data["data"]);
You don't need to test type of return value. You need to set type of return value in signature of method.
public JsonNetResult GetTemplateRootUnits(int templateId);