In one of my unit tests, I am having some difficulty getting a mocked method to executed. I have the following test code:
void testExample() {
def mockICFService = new MockFor(ICFService)
...
//Mock the methods
controller.metaClass.icfList = { def person ->
println "icfList"
return [new IC(conceptId:'12345')]
}
mockICFService.demand.getAllIC(1..1) { def id, def withHist, def appId ->
println "mocking service"
return new Person()
}
...
def model = controller.detail()
}
Inside of detail in my controller class I create a Person via the ICFService's getAllIC(). This part works correctly. Later in the function, however, there is a call to icfList (which is defined in the controller). Through println's I have determined that the call is still being made, although it is returning an empty array. I believe that this is because the array is populated based on data in the servletContext, but in Unit Testing there is no access to that (hence my trying to mock it out).
Does anyone know how to force the test to use the mocked version of controller.icfList instead of calling the actual method in controller?
When I try your code, what blows up for me is the mocked service, and the part that works properly is the mocked-out icfList() method. The opposite of your observation, interestingly. For what it's worth, here's what I did:
First replace new MockFor() class instantiation with the mockFor() method. Then you need to inject the mock service into the controller.
def mockICFService = mockFor(ICFService)
controller.iCFService = mockICFService.createMock()
By doing the above, only the mocked versions of icfList() and getAllIC() get called, so you are not using the servletContext at all. Check out the Grails testing documentation for more info.
Related
I am running my Junit test cases for my groovy class using spock framework I am using Mock to invoke my class. but it is giving me MissingMethodException but if I invoke the same method by normal creating object def obj = new MyClass() way it is working. please let me know am I missing something? below is my stacktrace
Expected no exception to be thrown, but got 'groovy.lang.MissingMethodException'
at spock.lang.Specification.noExceptionThrown(Specification.java:119)
at .AsplTest.fetchXmlTest(AsplTest.groovy:35)
Caused by: groovy.lang.MissingMethodException: No signature of method: com.webservice.Service.fetchAsplXml() is applicable for argument types: (java.lang.String, groovy.net.xmlrpc.XMLRPCServerProxy, java.lang.String) values: [3c98fa0dd1b5d92af599779bfb7be655, groovy.net.xmlrpc.XMLRPCServerProxy#797b0699, ...]
Possible solutions: getFetchAsplXml()
at .AsplTest.fetchXmlTest(AsplTest.groovy:33)
below is my test code
public void fetchXmlTest() {
given:
def asplObject=Mock(Service);
when:
asplObject.fetchXml(sessionId, serverProxy, "https://serverproxy")
then:
noExceptionThrown()
}
FYI:
my groovy version is 2.4.12 and spock version 1.1-groovy-2.4
It seems to me that you are doing things backwards.
Mocks are not test subjects. They are used to control the interactions of your test subjects with other objects. It looks, from the code you posted, that you want to test the invocation of method fetchXml on your Service object.
To do this, you need to create an instance of Service, and call the method. If your Service has collaborating objects, then you can Mock them, and add interactions, like this:
given:
def service = new Service()
and:
service.collaboratingObject = Mock(CollaboratingObjectClass)
when:
service.getFetchAsplXml()
then:
1 * service.collaboratingObject.someMethodReturningAString(_ as String) >> {String input-> "mockedResult from $input" as String }
I have a controller that on the save method calls a thread to retrieve some files. The thread has start() in a domain that has this line-
RetrievalThread retrievalThread = grailsApplication.mainContext.getBean ('retrievalThread').
In my unit test I tried this and it worked(I'll keep the other lines omitted that have no bearing right now). Without this line an error occurs saying can't get mainContext on null object,talking about grailsApplication. .
Def mainContext = Mock(ApplicationContext)
MainContext.getBean(_) >>{ name ->
return new MockRetrievalThread()}
The mock thread doesn't do anything.
This test runs fine but, any test after this fail with a null pointer exception with no real information. Looks like a bunch of background grails stuff. Is there a way to clean this up or use something better than what I'm using?
I'm sure there's a way to clean this up in a tearDown, but I think there is a better way.
1.) I would use DI rather than going through grailsApplication.mainContext.getBean; is there a reason you are doing it this way?
class MyController {
def retrievalThread
getFiles() {
return [files: retrievalThread.getFiles(params.id)]
}
}
2.a.) Using DI, you can then just set the controller's retrievalThread to a new instance of MockRetrievalThread within your test.
void "test getFiles"() {
given:
controller.retrievalThread = new MockRetrievalThread()
when:
params.id = 1
def returnedFiles = controller.getFiles()
then:
// assertions
}
2.b.) Or skip the MockRetrievalThread altogether and mock the retrievalThread bean using the mockFor method, and then set the mocked version to the injected instance in your controller.
void "test getFiles"() {
given:
def retrievalThreadMock = mockFor(RetrievalThread)
retrievalThreadMock.demand.getFiles { Integer input ->
return ['file1', 'file2', 'etc.']
}
controller.retrievalThread = retrievalThreadMock.createMock()
when:
params.id = 1
def returnedFiles = controller.getFiles()
then:
// assertions
}
You can use integration testing instead to run the entire app, in order to avoid any beans not being injected.
grails create-integration-test org.bookstore.Book
I have some often-used helper methods for unit tests put into a seperate file. The idea is to, for example, allow my XYZTests.groovy to call TestHelper.getUserObject() in order to get a fully initialized instance of User.
Now the problem is, that there's a springSecurityService.encodePassword(pw) being called in the User's beforeInsert() which always fails as there's no mock for springSecurityService in TestHelper.groovy.
java.lang.NullPointerException: Cannot invoke method encodePassword() on null object
In User.groovy:
def beforeInsert() {
// ...
password = springSecurityService.encodePassword(pw)
// ...
}
Note: I would like to avoid any mocking in TestHelper.groovy in order to use it's methods in integration tests too.
In spite of that, even if I try to call a mockFor() anywhere in the TestHelper.groovy, I get an MME:
No signature of method: static myproject.TestHelper.mockFor() is applicable for argument types: (java.lang.Class, java.lang.Boolean) values: [class grails.plugins.springsecurity.SpringSecurityService, true]
groovy.lang.MissingMethodException: No signature of method: static myproject.TestHelper.mockFor() is applicable for argument types: (java.lang.Class, java.lang.Boolean) values: [class grails.plugins.springsecurity.SpringSecurityService, true]
at myproject.TestHelper.mockSpringSecurityService(TestHelper.groovy:59)
at myproject.TestHelper$mockSpringSecurityService.callStatic(Unknown Source)
at myproject.TestHelper.getUserObject(TestHelper.groovy:47)
at myproject.TestHelper$getUserObject.call(Unknown Source)
at myproject.UserTests.setUp(UserTests.groovy:26)
Note: I currently mock the springSecurityService.encodePassword like this:
// in UserTests.groovy
protected void setUp() {
// mockDomain(...) and such here
def u = TestHelper.getUserObject("Pummel")
u.springSecurityService = mockSpringSecurityService()
assert u.save()
}
private mockSpringSecurityService() {
def ssService = mockFor(SpringSecurityService,true)
ssService.metaClass.encodePassword() { password ->
"08a2d3c63bf9fc88276d97a9e8df5f841fd772724ad10f119f7e516f228b74c6"
}
ssService
}
Any ideas on how I may be able to use the helper class while leaving all mocking in the unit tests only?
Where would I best place the TestHelper.groovy file for using it in integration AND unit tests?
Note that everything is working perfectly fine when I move all helpers into UserTests.groovy directly!
The solution to this is to refrain from calling any user.save() in the TestHelper.groovy.
This makes sense, as for many (unit) tests a persisted (saved) instance is unnecessary anyways.
On the other hand many cases actually require an unsaved intance. (In order to test certain effects of the .save() itself, for example)
A working example for integration tests would be:
def user = TestHelper.getUserObject()
user.save()
For a unit tests:
def user = TestHelper.getUserObject()
user.springSecurityService = new SpringSecurityService() // or the described mock accordingly
user.save()
This keeps any mocks out of TestHelper.groovy
In your TestHelper you can use Groovy ExpandoMetaClass metaClass.static to slap a mock closure for encodePassword on SpringSecurityService:
SpringSecurityService.metaClass.'static'.encodePassword = {'08a2d3c63bf9fc88276d97a9e8df5f841fd772724ad10f119f7e516f228b74c6'}
I would stick this class in a test package under src/groovy
I want to test a Grails controller which calls a service. I'd like to mock the service. The Service has a method:
JobIF JobServiceIF.getJob(int)
and JobIF has a method:
String JobIF.getTitle()
Here's my controller
def workActivities = {
JobIF job = jobService.getJob(params.id)
[career:job]
}
I understand that I need to mock the service and the job class (there are concrete implementations for both) but I'm struggling to get my head around the Groovy mocking object syntax. How do I mock a job and set the title to something, say "Architect" and then test the code?
So far I have:
void testWorkActivities() {
def controller = new CareersController()
... // Mocking stuff I don't know how to do
controller.params.id = 12
def model = controller.workActivities()
assertEquals "Architect", model["career"].getTitle()
}
You basically have two choices
Use the Groovy mocking classes, i.e. MockFor and StubFor
Use the Grails mock classes by calling the mockFor method of GrailsUnitTestCase. The class returned by this method is an instance of GrailsMock
Personally, I have found the Groovy mock objects to a bit more reliable than the Grails mocks. On occasions, I've found that my Grails mock objects were bypassed, even though I appeared to be setting everything up correctly.
Here's an example of how to use the Groovy mocks:
void testCreateSuccess() {
def controller = new CareersController()
// Create a mock for the JobService implementation class
def mockJobServiceFactory = new MockFor(JobService)
mockJobServiceFactory.demand.getJob {def id ->
// Return the instance of JobIF that is used when the mock is invoked
return new Job(title: "architect")
}
// Set the controller to use the mock service
controller.jobService = mockJobServiceFactory.proxyInstance()
// Do the test
controller.params.id = 12
def model = controller.workActivities()
assertEquals "Architect", model["career"].getTitle()
}
The process is basically the same when using the Grails mocks, but you call the mockFor method of the test class, instead of instantiating MockFor.
I am trying to test a Controller
that has a Command object with data binding.
The Command Object has a Service injected into it.
But When I try test the command object the injected service method
is never found as it is never "injected"
Is there a way to mock a service inside a command object?
Test method
void testLoginPasswordInvalid() {
mockRequest.method = 'POST'
mockDomain(User, [new User(login:"freddy", password:"realpassword")])
mockLogging(UserService) // userService mocked
MockUtils.prepareForConstraintsTests(LoginCommand)
def userService = new UserService()
def user = userService.getUser("freddy")//Gets called and returns the mockDomain
assert userService.getUser("freddy")//Passes
def cmd = new LoginCommand(login:"freddy", password:"letmein")
cmd.validate() // Fails (userService is nevr injected)
controller.login(cmd)
assertTrue cmd.hasErrors()
assertEquals "user.password.invalid", cmd.errors.password
assertEquals "/store/index", renderArgs.view
}
The getUser() method of the userService isn't found
Cannot invoke method getUser() on null object
java.lang.NullPointerException: Cannot invoke method getUser() on null object
Code
The login method of the controller being called,
def login = { LoginCommand cmd ->
if(request.method == 'POST') {
if(!cmd.hasErrors()){
session.user = cmd.getUser()
redirect(controller:'store')
}
else{
render(view:'/store/index', model:[loginCmd:cmd])
}
}else{
render(view:'/store/index')
}
}
The Command Object has a "userService" injected into it.
The validator calls this userService to find a user
class LoginCommand {
def userService
String login
String password
static constraints = {
login blank:false, validator:{ val, cmd ->
if(!cmd.userService.getUser()){
return "user.not.found"
}
}
}
The userService.getUser() looks like this.
class UserService {
boolean transactional = true
User getUser(String login) {
return User.findByLogin(login)
}
}
Service injection is done using Spring autowire-by-name. (Grep the Grails source tree for autowire to find a nice code fragment you can use to get it to autowire your controllers for you in integration tests.) This only functions in integration tests, where there's a Spring application context around that has the beans that can be injected.
In unit tests, you have to do this yourself since there's no Spring-land surrounding your stuff. This can be a pain, but gives you some benefits:
1) It's easy to inject mock versions of services - for example, using an Expando - in order to more closely specify the behavior of your controller's collaborating services, and to allow you to test only the controller logic rather than the controller and service together. (You can certainly do the latter in a unit test as well, but you have the choice of how to wire it up.)
2) It forces you to be explicit about the dependencies of your controller - if you depend on it, your tests will show it. This makes them a better specification for the behavior of your controller.
3) You can mock only the pieces of external collaborators your controller depends on. This helps your tests be less fragile - less likely to need to change when things change.
Short answer: your test method needs a cmd.userService = userService line.
What John says is on the mark. One example might be:
def mockUsers = [new User(login:"freddy", password:"realpassword")]
mockDomain(User, mockUsers)
def userService = [getUser:{String login -> mockUsers[0]}] as UserService
def cmd = new LoginCommand (/*arguments*/)
cmd.userService = userService
You can lookup other ways to mock objects at http://groovy.codehaus.org/Groovy+Mocks