I have a service that I'm trying to test. Inside this service is another UserAPIService that I want to mock. To mock it I'm doing the following:
given:
def userAPIServiceMock = mockFor(UserAPIService)
userAPIServiceMock.demand.createUser { def apiToken, firstname, lastname, email -> return true
}
service.userAPIService = userAPIServiceMock.createMock()
In the demand closure, I don't really care what arguments are passed to the createUser method. Is there a way I can say "regardless of the arguments passed to the createUser method, return true."
In other words, how can I change
userAPIServiceMock.demand.createUser { def apiToken, firstname, lastname, email -> return true
to
userAPIServiceMock.demand.createUser { [any arguments] -> return true
This is possible with Spock.
The syntax is as follows:
mock.method(*_) >> { args -> true }
Not sure how your Grails service needs to be mocked, but here's a full, general example in Spock:
interface Service {
boolean hej( String s, boolean b, char c )
}
class ExampleSpec extends Specification {
def "mock method with any number of args"() {
when:
def mock = Mock( Service )
mock.hej(*_) >> { args -> true }
then:
mock.hej( 'hi', true, 'a' as char ) == true
}
}
args is a List containing the actual arguments, which you can inspect in the closure and return the appropriate value.
Related
I wrote service for manual requeuing events from one queue to another.
public class ReQueueService {
private final RabbitTemplate rabbitTemplate;
public void retry() {
InfoLog infoLog;
while (rabbitTemplate != null &&
(infoLog = (InfoLog) rabbitTemplate.receiveAndConvert(EVENT_WAITING_FOR_REQUEUE)) != null
) {
rabbitTemplate.convertAndSend(SOME_QUEUE, infoLog.getSomeEvent());
}
}
}
The problem I am facing is getting:
Too many invocations for:
1 * rabbitTemplate.convertAndSend(SOME_QUEUE, _ as SomeEvent) >> {
arguments ->
assert infoLog.getSomeEvent() == arguments[1]
} (2 invocations)
Matching invocations (ordered by last occurrence):
2 * rabbitTemplate.convertAndSend(SOME_QUEUE, ...
while my code in test looks like this:
class ReQueueServiceTest extends Specification {
def "should resend single event to some queue" () {
given:
InfoLog infoLog = Fixtures.createInfoLog()
def rabbitTemplate = Mock(RabbitTemplate){
receiveAndConvert(EVENT_WAITING_FOR_REQUEUE) >> { infoLog }
}
ReQueueService reSyncService = new ReQueueService(rabbitTemplate)
when:
reSyncService.retry()
then:
1 * rabbitTemplate.convertAndSend(SOME_QUEUE, _ as SomeEvent) >> {
arguments ->
assert infoLog.getSomeEvent() == arguments[1]
}
}
}
The question is why I have 2 invocations, if I stubb only one event?
EDIT:
link to repo with example: https://gitlab.com/bartekwichowski/spock-too-many
Thanks for the repo link. As soon as I could run the test and inspect the behaviour live, it was pretty easy to find out what was wrong. First I will make an educated guess about what you actually want to test:
The mock's receiveAndConvert method should return str when it is called first and then null when called again.
Subsequently you want to verify that the while loop runs exactly 1 iteration, i.e. that convertAndSend is called with exactly the parameters you expect.
This can be achieved by
receiveAndConvert("FOO") >>> [str, null]
1 * rabbitTemplate.convertAndSend("BAR", str) (no need for ugly assertions inside a stubbed method, the parameters are verified against your parameter constraints already)
If I refactor your specification a little bit for prettier variable names and less verbosity, it looks like this:
class ReSyncServiceTest extends Specification {
def "should resend single event to resource sync queue"() {
given:
def message = "someValue"
def rabbitTemplate = Mock(RabbitTemplate) {
receiveAndConvert("FOO") >>> [message, null]
}
when:
new ReSyncService(rabbitTemplate).retry()
then:
1 * rabbitTemplate.convertAndSend("BAR", message)
}
}
P.S.: Your version with the assertion inside does not return anything explicitly, but implicitly the result of the last assertion. Be careful with that. With >> { ... } you are stubbing the method result! It would always return true in the version you have in Git and the test only terminates because you added the 1 * limit. If it was not there, you would have an endless loop. Your code did not do what you thought it did. Maybe the Spock manual can help you there. :-)
P.P.S.: Maybe you want to refactor your application code to be a bit easier to understand and maintain and to be a little less "smart". Also there is no need to check that rabbitTemplate != null in every iteration, once should be enough. How about this?
#Slf4j
#Service
#AllArgsConstructor
public class ReSyncService {
private final RabbitTemplate rabbitTemplate;
public void retry() {
if (rabbitTemplate == null)
return;
String event;
while (null != (event = getEventFromQueue()))
rabbitTemplate.convertAndSend("BAR", event);
}
protected String getEventFromQueue() {
return (String) rabbitTemplate.receiveAndConvert("FOO");
}
}
I have many queries that have the same logic and I decided to extract it into closure.
Here is an example:
Closure whereByProjectIdAndUser = { Criteria cr, Long projectId, User user ->
Long userId = user.id
Boolean isReviewer = user.isReviewer()
cr.isNull 'project.deletedAt'
cr.eq 'project.id', projectId
(cr | {
cr.eq 'owner.id', userId
if (isReviewer) {
cr & {
cr.eq 'reviewer.id', userId
cr.ne 'project.certificationStatus', ProjectCertificationStatus.None
}
}
})
}
#Transactional(readOnly = true)
RrmUtilization[] getAllByProjectIdAndUser(Long projectId, User user) {
BuildableCriteria cr = RrmUtilization.createCriteria()
RrmUtilization[] result = cr.list {
createAlias('project', 'project', JoinType.INNER_JOIN)
createAlias('project.owner', 'owner', JoinType.INNER_JOIN)
createAlias('project.reviewer', 'reviewer', JoinType.LEFT_OUTER_JOIN)
isNull 'deletedAt'
whereByProjectIdAndUser(cr, projectId, user)
} as RrmUtilization[]
result
}
This works just fine, however, I was trying to understand how can I avoid explicitly passing BuildableCriteria into the closure? Is there a way to have it implicitly passed through and get a nicer code?
Maybe there are some better approaches in general?
You can use the keyword this to access parameters in the enclosing class of the closure.
Groovy documentation: http://groovy-lang.org/closures.html#closure-owner
I am stuck somewhere in Grails 3 spock Testing for Taglibs.
I want to test a closure of taglib, which looks like:
ATagLib.groovy:
Closure a = {attrs ->
java.util.TimeZone utc = java.util.TimeZone.getTimeZone(FraudnetConstants.TIMEZONE_UTC)
if (attrs.somevar) {
out << "${g.message(code: 'some.code')} ${g.formatDate([date: attrs.date, format: "h:mma", timeZone: utc])}"
} else if (attrs.freq == SomeConstants.VAL || attrs.freq == SomeConstants.VAL) {
out << g.formatDate([date: attrs.date, format: "E '#' h:mma", timeZone: utc])
} else {
out << "${g.message(code: 'some.other.code')} ${g.formatDate([date: attrs.date, format: "h:mma", timeZone: utc])}"
}
}
My ATagLibSpec.groovy looks like:
#TestFor(ATagLib)
class ATagLibSpec {
def setup() {
java.util.TimeZone.metaClass.'static'.getTimeZone = {a -> return null }
}
void 'testmethod'() {
expect:
taglib.a()
}
}
The exception i am getting while running testcases is:
java.lang.NullPointerException: Cannot invoke method getTimeZone() on null object at org.grails.plugins.web.taglib.FormatTagLib$_closure2.doCall(FormatTagLib.groovy:170) at groovy.lang.Closure.call(Closure.java:414)
at org.grails.taglib.TagOutput.captureTagOutput(TagOutput.java:64)
at org.grails.taglib.TagLibraryMetaUtils.methodMissingForTagLib(TagLibraryMetaUtils.groovy:138)
at org.grails.taglib.NamespacedTagDispatcher.methodMissing(NamespacedTagDispatcher.groovy:59)
Can someone point here, what's wrong with the above way of prepopulating getTimeZone.
instead of using metaprogramming, it is best to inject a TimezoneFactoryService into your taglib. Having to metaprogram in a test is in my experience an indication of code smell: your code uses a static method to instantiate an object, instead of dependency injection.
Your code could look like this:
Closure a = {attrs->
java.util.TimeZone utc = timezoneFactoryService.getTimeZone(FraudnetConstants.TIMEZONE_UTC)
}
This will allow you to mock your factory service in your spec in a way more convenient way, by using a regular Spock Mock.
#TestFor(ATagLib)
class ATagLibSpec {
def setup() {
taglib.timezoneFactoryService=Stub(TimezoneFactoryService) {
getTimeZone(_) >> null
}
}
void 'testmethod'() {
expect:
taglib.a()
}
}
If you still want to use metaprogramming, be aware that the signature of the method has to match fully (also the parameter types), so:
java.util.TimeZone.metaClass.'static'.getTimeZone = {a -> return null }
java.util.TimeZone.getTimeZone("America/Los_Angeles")
This code will get the timezone for America/Los Angeles, but
java.util.TimeZone.metaClass.'static'.getTimeZone = {String a -> return null }
java.util.TimeZone.getTimeZone("America/Los_Angeles")
this one will return null as we have modified the method properly via metaprogramming.
Be also aware that you have to use the injected variable tagLib not taglib.
I am trying to mock the groovy.sql.Sql call(query, params[], closure) class.
Below is method, within a DatabaseService class file, that I am attempting this on.
public void getUsers(List<User> developerList, Sql sql) {
sql.call("{? = call GETUSERS()}", [Sql.resultSet(OracleTypes.CURSOR)]) { result ->
while (result.next()) {
User user = new User()
user .email = result.EMAIL
user .lastName = result.LASTNAME
}
}
}
My mock does achieve the task, however, I do not want the mocked closure to execute. I want to mock the .call(,,_) method to only skip the database logic, and return back a list to the closure in the getUsers() method. I want the closure to execute in getUsers() method, not the mocked up method.
Below is the mockup I have written in SPOCK.
void "test getUsers(list,sql) some results"() {
DataSource mockedSource = Mock(DataSource)
Sql mockedSql = Mock(Sql)
DatabaseService databaseService = new DatabaseService()
databaseService.dataSource = mockedSource
List<User> userList= new ArrayList<>();
when:
databaseService.getUsers(userList, mockedSql)
then:
1 * mockedSql.call(_, _, _) >> { return [[EMAIL: "A", LASTNAME: "B"]] }
userList.size() == 1
}
As imagined, this mockup overwrites the original method closure, and my list is never populated.. I certainly do not want to rewrite my class to use Java, nor can I change the stored procedure that is executed.
try :
int resultSetIdx = 0
def resutSet = Mock(ResultSet)
...
then:
1 * mockedSql.call(_, _, _) >> { args -> args[2].call(resultSet) }
2 * mockedResultset.next() >> { ++resultSetIdx > 1 ? false: true}
1 * mockedResultset.getString("EMAIL") >> "A"
In the getUsers method() change
user.lastName = result.LASTNAME
user.email = result.EMAIL
To
user.lastName = result.getString("LASTNAME")
user.email = result.getString("EMAIL")
However, you shouldn't mock Sql, but rewrite your service/dao layer to be more testable. test the dao with an inmemory db, and the service layer with a mocked dao.
I have a class with method save():
public class FilesystemImagePersistenceStrategy implements ImagePersistenceStrategy {
public void save(MultipartFile file, Image image) {
File dest = createFile(image);
writeToFile(file, dest);
}
// protected to allow spying
protected void writeToFile(MultipartFile file, File dest) throws IOException {
IOUtils.copy(file.getInputStream(), new FileOutputStream(dest));
}
}
And now I want to check name of the file before it will be saved:
class FilesystemImagePersistenceStrategyTest extends Specification {
private ImagePersistenceStrategy strategy = Spy(FilesystemImagePersistenceStrategy)
private MultipartFile multipartFile = Mock()
private Image image = TestObjects.createImage()
def "save() should gives proper name to the file"() {
given:
String expectedFileName = ...
when:
strategy.save(multipartFile, image)
then:
1 * strategy.writeToFile({ MultipartFile file, File dest ->
assert dest.name == expectedFileName
return true
})
}
}
But unfortunately it doesn't work, real method invoked instead...
Why it so? And how to check method's argument?
P.S. I also provided an example at http://meetspock.appspot.com/script/5741031244955648
I ended up with the following:
1 * strategy.writeToFile(
multipartFile,
{ assert it.name == expectedFileName; return true }
) >> {}
There assert call needs to show me nice error message, like this:
it.name != expectedFileName
| | | |
| 1.png| 2.png
| false
/tmp/1.png
Also return true is required for case when test fails. Without this statement closure will return false and this method will not be accounted as candidate (and real method will be executed).
The argument constraint specified for 1 * strategy.writeToFile is wrong. There needs to be one constraint per argument, and constraints must not contain assert statements. Additionally, if a spy is to be used as a partial mock, the default response (which calls through to the real method) needs to be suppressed by supplying a stubbed response ("do nothing" in this case). This leads to:
...
then:
1 * strategy.writeToFile(multipartFile, { it.name == expectedFileName }) >> {}
Which can be simplified to:
...
then:
1 * strategy.writeToFile(multipartFile, expectedFileName) >> {}
for multiplicity and assertions on mock, I used the following:
then:
numberOfCalls * mock.accept(_) >> {ArgType arg ->
assert arg.id == my.id
assert arg.minSerial == my.min()
assert arg.maxSerial == my.max()
}