Attempting to unit test a Grails 2.1.x controller that calls a template to show a list of history items with a status. This controller works fine in manual testing but were attempting to automate things and I'm getting stuck on this part. Part of our issue may be that the domain object is over engineered.
The setup for this test may be more integration rather than unit testing but I'm not sure I can test the function with out it.
The controller action generates a list of history items via a createCriteria query. This list is passed to the template to be rendered.
def loadHistValues(){
def histDomainObject = new historyDom()
def elements = histDomainObject.createCriteria().list(max: params.max, offset: params.offset)
render (template: 'searchResults', model:[elements: elements]
}
The template has code that iterates through the list putting values in each column. One of these items is getStatus(). This calls a utility service to return the values.
def getStatus(){
return historyUtillityService.getStatus(this)
}
The service gets the latest history event and returns the value.
def getStatus(HistoryDom hist){
def histStatus = HistoryEvent.createCriteria().get(
maxResults(1)
order('id', 'desc')
eq('historyDom', hist)
)
if (histStatus == null)
return 0
else
return histStatus.status
}
I'm getting a null pointer when the getStatus() is called.
I've setup both mock domain object and mock services but I'm not sure that these are getting down to this level or maybe I'm calling them wrong.
#TestFor (HistoryController)
#MockFor (HistoryDom, HistoryEventDom)
class HistoryControllerTests{
def util = new UnitTestUtil()
void testLoadHistValues(){
def mockHistoryUtilityService = mockfor (HistoryUtilityService)
mockHisotryUtilityService.demand.getStatus (-> return Status.QUEUED)
def histObj1 = util.initMockHistObj(1)
def histObj2 = util.initMockHistObj(2)
histObj1.save()
histObj2.save()
def mockHistEvent = new HistEvent(
histDate: histObj1.getHistDate(),
histObj: histObj1,
histStatus: Status.QUEUED
)
mockHistEvent.save()
controller.loadHistValues()
assert response.text contains("Something worth testing")
}
I tried setting a mock view before the call to the controller and checking the response text for that but it never gets past the call to the controller since its still trying to process the template. I'm at a loss at this point as to how to test this controller function, or is it that the object wasn’t architected properly; this seems a bit overly complicated.
answer was to mock things for constrainsts tests before they would get fully saved by mock GORM. I guess mockForConstraintsTests dosnt quite function as I expected
Related
I have a need for a uniqueID within my Django code. I wrote a simple model like this
class UniqueIDGenerator(models.Model):
nextID = models.PositiveIntegerField(blank=False)
#classmethod
def getNextID(self):
if(self.objects.filter(id=1).exists()):
idValue = self.objects.get(id=1).nextID
idValue += 1
self.objects.filter(id=1).update(nextID=idValue)
return idValue
tempObj = self(nextID=1)
tempObj.save()
return tempObj.nextID
Then I wrote a unit test like this:
class ModelWorking(TestCase):
def setUp(self):
return None
def test_IDGenerator(self):
returnValue = UniqueIDGenerator.getNextID()
self.assertEqual(returnValue, 1)
returnValue = UniqueIDGenerator.getNextID()
self.assertEqual(returnValue, 2)
return None
When I run this test by itself, it runs fine. No issues.
When I run this test as a suite, which includes a bunch of other unit tests as well (which include calls to getNextID() as well), this test fails. The getNextID() always returns 1. Why would that be happening?
I figured it out.
Django runs each test in a transaction to provide isolation. Doc link.
Since my other tests make a call to getNextID(), the first row gets deleted after the first test that makes such a call is complete. Subsequent tests never find (id=1), due to which all subsequent calls return the value 1.
Even though I don't think I would face that situation in production, I went I ahead and changed my code to use .first() instead of (id=1). Like this
def getNextID(self):
firstRow = self.objects.first()
if(firstRow):
That way I believe it would better handle any future scenario when the database table might be emptied.
Let's say I have a Fastapi application like this (This code is taken from documentations):
app = FastAPI()
#app.get("/foo")
async def read_main():
return {"msg": "Hello World"}
I believe there are two ways of testing this view. The first one is by the use of a client object. For instance:
client = TestClient(app)
def test_read_main():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"msg": "Hello World"}
However, I think this is not a unit test. It is more like an integration test. Because we are actually running fastapi codes as well.
The second option which in my opinion is closer to unit test definition is to run the function directly:
def test_read_main():
response = read_main()
assert response == {"msg": "Hello World"}
Similarly, in Django, we can directly call the view function, or by using a client object.
My first question is which one is better?
Let's say we chose one of them and now in order to prevent calling the database I have mocked the database. Now I want to test a view that just checks if something exists in the database. My second question is what is the point of testing such view? Becuase after mocking the database, we know what would happen when calling the database with given arguments.
I've got a couple models, Car and CertifyRequest. When a Car instance is created, modified or deleted I need to create a CertifyRequest, which in turn needs to be manually approved. The CertifyRequest instance contains the time it was created.
I've tested creating and modifying by injecting context={"now": …} into a CarSerializer instance, but I can't figure out how to do the equivalent when deleting:
Delete requests are never passed to the serializer, so I can't access the context in the same way.
I can override destroy in the ModelViewSet and use get_serializer_context within it, but
I can't seem to pass the serializer to the ModelViewSet instance and
the implementation returns a completely different context anyway.
I do not want to use a horrible hack like an optional query parameter or testing that the time is "close to" the current test client time.
The hack I'm using currently is to set an extra now property on the Request which I pass to the view, and to look for that inside destroy.
If you're using Django's timezone.now() in your view to get the current time, you can mock that method to return a specific time in your tests and assert against that.
def test_destroy_car():
client = APIClient()
#mock.patch("application.views.timezone.now") as now:
destroy_time = datetime.datetime(2019, 4, 23, 11, 2, 0)
now.return_value = destroy_time
response = client.destroy("/api/car/12345/")
assert response.status_code == status.HTTP_204_NO_CONTENT, "The request to delete did not return a 204 response"
certify_request = CertifyRequest.objects.order_by("id").last()
assert certify_request.created_at == destroy_time, "CertifyRequest destroy time is incorrect"
I'm building unit tests for this webapp2 handler (built for GAE)
class PushNotificationHandler(webapp2.RequestHandler):
def post(self):
UserNotification.parse_from_queue(self.request)
return
app = webapp2.WSGIApplication([
(r'/push/notification', PushNotificationHandler),
], debug=True)
One test is
#patch.object(UserNotification, 'parse_from_queue')
def test_post_webapp(self, p_parse_from_queue):
response = webtest.TestApp(app).post('/push/notification')
eq_(response.status_int, 200)
p_parse_from_queue.assert_called_once_with(response.request)
The HTTP reply is OK, but the mock assertion fails:
Expected call: parse_from_queue(<TestRequest at 0x105a89850 POST http://localhost/push/notification>)
Actual call: parse_from_queue(<Request at 0x105a89950 POST http://localhost/push/notification>)
I can't understand why the request is not the correct one (looks like a deep copy). Is there anything wrong with the unit-test, or is that the way webapp2 handle requests. In the second case, is there a way to test it, without creating a separate test to test PushNotificationHandler.post()
Thanks
I've used mock's call_args in a similar situation. You can do something like this:
request = p_parse_from_queue.call_args[0][0]
self.assertEqual(request.url, "foo")
self.assertEqual(request.*, *)
The [0][0] gives you the first passed argument assuming that you are using ordered arguments and not keyword arguments.
You can then proceed to check other relevant attributes of the request object to make sure it is behaving as expected.
I am trying to get unit testing to work for scaffolding. There are a few changes that I made to the original scaffolding template to serve what I needed for my application. This is the code for the test:
void "Test that the show action returns the correct model"() {
when:"A domain instance is passed to the show action"
populateValidParams(params)
def domainName = new DomainName(params)
controller.show(domain.toString())
then:"A model is populated containing the domain instance"
model.domainNameInstance == domainName
}
This is the code for the show action, where I grab the domain instance from the database based on two values within the id, which is created by a toString method within the controller:
def show(String id){
if (id!=null){
def (term, college)=id.split('_')
DomainName domainNameInstance
respond domainNameInstance=DomainName.findByVal1AndVal2(val1, val2)
}else{
response.sendError(HttpServletResponse.SC_NOT_FOUND)
}
}
The issue that I am having is that when I run the test, the domainInstance is null within the test, whereas the domain is returning the right values. Would someone know what I need to change to make the domainInstance inside the test not null? For reference, I am using Grails 2.4.3 and the Scaffolding 2.1.2 plugin.
In your unit test, you need to save the DomainName instance you are creating. Your show action is querying the database but you haven't persisted the test instance.