I'm trying to test search method in my grails app but I'm having a null pointer exception. I mocked the domain in my test as follows:
#TestFor(AuditController)
#Mock(Audit)
class AuditControllerTests {
void testSearch() {
populateValidParams(params)
def audit=new Audit(params)
audit.save(failOnError: true)
//Search existing customer
def model = controller.search()
assert model.auditInstanceList.size() == 1
assert model.auditInstanceList.size() == 1
}
}
I got NPE on model.auditInstanceList. Where it shouldn't be null. Here is the code in my controller:
def search = {
def query
def criteria = Audit.createCriteria()
def results
query = {
and{
if(params.customerName){
ilike("customerName", params.customer + '%')
}
if(params.siteName){
ilike("siteName", params.siteName + '%')
}
max:params.max
offset:params.offset
}
}
results = criteria.list(params, query)
render(view:'list', model:[ auditInstanceList: results,auditInstanceTotal:results.totalCount ])
}
What is going on with this?
Return model at the end of search. As in,
def search = {
...
render(view:'list', model:[ auditInstanceList: results, auditInstanceTotal:results.totalCount ])
[auditInstanceList: results, auditInstanceTotal:results.totalCount]
}
When testing a controller action that calls render(), model and view variables are automatically created and populated in your test. By doing def model = controller.search(), you are replacing the magic model with your own, assigning it to the return value of search(). The correct way to do your assertions is:
controller.search()
assert model.auditInstanceList.size() == 1
assert view == '/audit/list'
I don't know why but sometimes you need to remove model as a return value from controller's action. I use both version alternatively in case one of them fails:
// sometimes this one works
def model = controller.search()
assert model
// sometimes this one works
controller.search()
assert model
Edit: I think of two new possibilities why your action doesn't work:
try to change your action from closure and make it a method.
make sure you have no after filter. I've found this bug: http://jira.grails.org/browse/GRAILS-6825
Related
I have some tests for functions that use cache, for example:
Function:
#retry(stop=stop_after_attempt(3))
#cache.cached(timeout=60, key_prefix='resouce_group_list')
def get_azure_resource_groups():
data = []
resource_client = get_azure_resource_client()
for item in resource_client.resource_groups.list():
data.append(item)
return data
Test:
#patch("dev_maintenance.machines.get_azure_resource_client")
def test_get_azure_list_rg(get_azure_resource_client):
cache.clear()
data = []
with app.app_context():
ret = get_azure_resource_groups()
get_azure_resource_client.assert_called_once()
expected = get_azure_resource_client.return_value.resource_groups.list.return_value
assert len(get_azure_resource_client.return_value.method_calls) == 1
for item in expected:
data.append(item)
assert ret == data
cache.clear()
The above test works fine, it passes, no errors and the test is using cache.
But i got other tests, and the decorator here does not matter, it will give the same error if i change the decorator to #cache.cache:
Function:
#retry(stop=stop_after_attempt(3))
#cache.memoize(60)
def get_azure_machine_info(rg_name, machine_name, expand="instanceView"):
try:
compute_client = get_azure_compute_client()
return compute_client.virtual_machines.get(rg_name, machine_name, expand=expand)
except CloudError:
return None
Test:
#patch("dev_maintenance.machines.get_azure_compute_client")
def test_get_azure_machine_info (get_azure_compute_client):
cache.delete_memoized(get_azure_machine_info)
with app.app_context():
ret = get_azure_machine_info("rg1", "m1")
print(ret)
get_azure_compute_client.assert_called_once()
assert len(get_azure_compute_client.return_value.method_calls) == 1
assert (
ret == get_azure_compute_client.return_value.virtual_machines.get.return_value
)
get_azure_compute_client.return_value.virtual_machines.get.assert_called_once_with(
"rg1", "m1", expand="instanceView"
)
cache.delete_memoized(get_azure_machine_info)
Now here the test fails with the error on this line ret = get_azure_machine_info("rg1", "m1"):
value = None, from_value = PicklingError("Can't pickle <class 'unittest.mock.MagicMock'>: it's not the same object as unittest.mock.MagicMock")
> ???
E tenacity.RetryError: RetryError[<Future at 0x105c7c3d0 state=finished raised PicklingError>]
<string>:3: RetryError
I tried to mock the cache passing a patch decorator like:
#patch("dev_maintenance.machines.cache") or #patch("dev_maintenance.cache")
I tried to set the CACHE_TYPE to null in the test case, instantiating the cache object and passing the config:
cache = Cache()
cache.init_app(app, config={"CACHE_TYPE": "redis"})
but no success so far, any help?
This is a reference to an old answer, but I think that generally MagicMock objects aren't meant to be pickled: https://github.com/thadeusb/flask-cache/issues/52
That error message is different though, and this is more similar to what you are seeing:
Is there a way to make python pickle ignore "it's not the same object " errors
Maybe you could replace the domain prefix to the class like the answer above, but I am not sure it will overcome the other difficulties of pickling a MagicMock class:
`#patch("__main__.get_azure_compute_client")`
I'am writing the answer here for people that need to test functions that are cached with flask-caching and have the same error then me.
What i needed was to create an Object inside the test and make the mock_value = Object like this:
First i create a simple class:
class MachineInfo(object):
pass
Then in my test:
#patch("dev_maintenance.machines.get_azure_compute_client")
def test_get_azure_machine_info (get_azure_compute_client):
cache.clear()
expected_res = MachineInfo()
expected_res.id = "id"
expected_res.name = "machine1"
expected_res.location = "location"
expected_res.hardware_profile = "hardware"
expected_res.storage_profile = "storage"
expected_res.network_profile = "network_profile"
get_azure_compute_client.return_value.virtual_machines.get.return_value = expected_res
res = get_azure_machine_info("rg1", "m1")
assert res == expected_res
cache.clear()
Then i could assert function_call() == Object or function_call() == mock.return_value
This simulates what the actual azure returns, an object, so i just make the mock return the object that i created so i can simulate the function itself.
In the below class, I just need RevShareFormula.withCriteria to return a result,
but getting the exception in resultTransformer() method.
Can anyone tell me how to Mock the below method so that i get some result from withCriteria
Here is the class:
class PartnerFinancialService {
def getPartnerPayeeRevenuShareDetails(long partnerPayeeId, def contextTypeCode) {
def partnerPayeesRevShareFormula = RevShareFormula.withCriteria {
resultTransformer(CriteriaSpecification.ALIAS_TO_ENTITY_MAP)
createAlias('partnerRevShareConfig', 'partnerRevShareConfig')
createAlias('pricingModel', 'pricingModel')
createAlias('partnerRevShareConfig.revshareCategory', 'revshareCategory')
and {
eq("revshareCategory.payeeProfileId", partnerPayeeId)
eq("revshareCategory.referenceContextTypeCode", contextTypeCode)
isNull("partnerRevShareConfig.revshareValidToDate")
}
projections {
property("id", "formulaId")
property("pricingModel.id", "pricingModelId")
property("pricingModel.pricingName", "pricingName")
property("pricingModel.pricingType", "pricingType")
..
..
}
}
}
Here is the test class
#TestFor(PartnerFinancialService)
#Mock(RevShareFormula)
class PartnerFinancialServiceSpec extends Specification {
void "test getPartnerPayeeRevShareDetails"() {
def partnerPayeeRevShare = new PartnerRevShareConfig()
partnerPayeeRevShare.id = 1
def revShareModel = new PricingModel();
revShareModel.id = 1
def partnerPayeeRevShareFormula = new RevShareFormula();
partnerPayeeRevShareFormula.id=5
partnerPayeeRevShareFormula.pricingModel = revShareModel
partnerPayeeRevShareFormula.partnerRevShareConfig = partnerPayeeRevShare
partnerPayeeRevShareFormula.revshareFormula = "revshare*10"
partnerPayeeRevShareFormula.revshareTierHighValue = 0
partnerPayeeRevShareFormula.revshareTierLowValue= 0
RevShareFormula.metaClass.static.withCriteria = {partnerPayeeRevShareFormula}
when:
def result = service.getPartnerPayeeRevenuShareDetails(1,"PKG")
then:
//assert result.pricingModel.id == 1
println "Succesfully Fetched from DB"
}
}
Getting the following exception.
<testcase classname="com.orbitz.dat.partners.PartnerFinancialServiceSpec" name="test getPartnerPayeeRevShareDetails" time="0.039">
<error message="No signature of method: com.orbitz.dat.partners.PartnerFinancialService.resultTransformer() is applicable for argument types: (org.hibernate.transform.AliasToEntityMapResultTransformer) values: [org.hibernate.transform.AliasToEntityMapResultTransformer#3632aa4]" type="groovy.lang.MissingMethodException">groovy.lang.MissingMethodException: No signature of method: com.orbitz.dat.partners.PartnerFinancialService.resultTransformer() is applicable for argument types: (org.hibernate.transform.AliasToEntityMapResultTransformer) values: [org.hibernate.transform.AliasToEntityMapResultTransformer#3632aa4]
at com.orbitz.dat.partners.PartnerFinancialService.$tt__getPartnerPayeeRevenuShareDetails_closure24(PartnerFinancialService.groovy:39)
at grails.gorm.CriteriaBuilder.invokeClosureNode(CriteriaBuilder.java:1093)
at grails.gorm.CriteriaBuilder.invokeMethod(CriteriaBuilder.java:314)
at org.grails.datastore.gorm.GormStaticApi.withCriteria_closure11(GormStaticApi.groovy:304)
at org.grails.datastore.mapping.core.DatastoreUtils.execute(DatastoreUtils.java:302)
at org.grails.datastore.gorm.AbstractDatastoreApi.execute(AbstractDatastoreApi.groovy:37)
at org.grails.datastore.gorm.GormStaticApi.withCriteria(GormStaticApi.groovy:303)
at com.orbitz.dat.partners.PartnerFinancialService.$tt__getPartnerPayeeRevenuShareDetails(PartnerFinancialService.groovy:38)
at com.orbitz.dat.partners.PartnerFinancialServiceSpec.test getPartnerPayeeRevShareDetails(PartnerFinancialServiceSpec.groovy:71)
Use an integration test. Never test ORM code with unit tests. I know it's slower and the experience is less enjoyable, but you are fooling yourself if you think that you are actually testing something related to database queries with this test. You are testing the testing framework of grails (the in-memory GORM implementation)
I have this piece of code in a controller:
def update = {
Map model = [:]
model.foo = params.foo
model.bar = params.bar
def result = ""
MyObject obj = MyObject.findWhere(bar:bar, foo:foo)
MyObjectService.updateObj(model,obj)
result = true
render result as JSON
}
And this simple unit test:
def 'controller update'() {
given:
controller.params.foo = foo
controller.params.bar = bar
MyObject obj = new MyObject(bar:bar, foo:foo)
mockDomain(MyObject,[obj])
when:
controller.update()
then:
1 * MyObject.findWhere(bar:bar, foo:foo) >> obj
1 * MyObjectService.updateObj(model,obj)
and:
def model = JSON.parse(controller.response.contentAsString)
model == true
where:
foo = "0"
bar = "1"
}
Now this is failing by and it is telling me that, "not static method findWhere is applicable..." for those arguments. That "MyObject" is just an orm class, and when I run that application everything seems to be working fine, but the test is failing.
My logic is this:
I want to count how many times the findWhere and updateObj methods are call and I am also mocking their response. So findWhere will return the object I already mocked, and pass it on to the service.
Any ideas why this is failing ?
For mocking static methods you should use Spock's GroovyStub class which introduced in v0.7.
I've got an action in my controller:
def dirtyMarker() {
render(template: '/layouts/modals/marker/dirtyMarker')
}
and Id like to unit test it. Ive been trying lots of possibilities. This may seem simple, but nothing seems to work (Grails 2.2.3). I know that here, testing might not be important but Ive got lots of other methods that returns a rendered template and I dont know how to implement this test..
seems to me that this should work:
void dirtyMarker() {
controller.metaClass.render = { Map params ->
assert params.template == '/layouts/modals/marker/dirtyMarker'
return 'a'
}
def result = controller.dirtyMarker()
assert result == 'a'
}
You can also mock the template:
void testDirtyMarker() {
views['/layouts/modals/marker/_dirtyMarker.gsp'] = 'mock contents'
controller.dirtyMarker()
assert response.text == 'mock contents'
}
See Testing Template Rendering for details
def retrieveEatenFood(String token, String addedDate) {
def consumer = Consumer.findByMobileToken(token)
if(consumer != null) {
def efList = []
def list = consumer.findAll("from EatenFood as ef where date(ef.dateAdded) = date(:da)",[da:sdf_long.parse(addedDate)])
list.each{
def eatenList = [:]
eatenList.put("foodType",it.food.name)
eatenList.put("sequenceNumber",it.sequenceNumber)
eatenList.put("eatenDate", it.eatenDate)
eatenList.put("DateAdded",it.dateAdded)
efList.add(eatenList);
}
return efList;
}
}
Trying to mock the above method, but the findAll keep generating an exception.
This issue it works! Now i need to write the test for it and i keep getting this exception. Can anyone help me please!
groovy.lang.MissingMethodException: No signature of method: carrotdev.Consumer.findAll() is applicable for argument types: (java.lang.String, java.util.LinkedHashMap) values: [from EatenFood as ef where date(ef.dateAdded) = date(:da), [da:Sun Feb 13 01:51:47 AST 2011]]
Possible solutions: findAll(groovy.lang.Closure), find(groovy.lang.Closure)
at carrotdev.ConsumerService.retrieveEatenFood(ConsumerService.groovy:146)
at carrotdev.ConsumerService$retrieveEatenFood.call(Unknown Source)
at carrotdev.ConsumerServiceTests.testEatenFoodRetrievedSucessfully(ConsumerServiceTests.groovy:359)
I would move the query to the Consumer domain class with a descriptive name, e.g.
static List<EatenFood> findAllEatenByDate(String date) {
consumer.findAll(
"from EatenFood as ef where date(ef.dateAdded) = date(:da)",
[da:sdf_long.parse(addedDate)])
}
Then the call is simply
def list = Consumer.findAllEatenByDate(addedDate)
and you can mock that easily with
def foods = [new EatenFood(...), new EatenFood(...), ...]
Consumer.metaClass.static.findAllEatenByDate = { String date - > foods }
Be sure to test the finder method in your Consumer integration test.