0

I have a test like this

def "fileField should be set for each line batch"(){
    given:
    LuceneEngine le = new LuceneEngine()
    le.indexWriter = Mock( IndexWriter ){
        addDocument(_) >> null 
    }
    le.currentFilename  = 'dummy filename'
    le.fileField = GroovyMock( TextField )

    when:
    le.processLineBatch([ 'dummy text' ], 0 )

    then:
    1 * le.fileField.setStringValue( 'dummy filename' ) >> null         

}

The app method looks like this:

def processLineBatch( List lineBatch, int deliveryNo ) {
    String lDocText = lineBatch.join( '\n' ).trim()
    textField.setStringValue( lDocText )
    fileField.setStringValue( currentFilename )
    indexWriter.addDocument( singleLDoc )
}

I have to use GroovyMock for TextField because the class is final.

Whatever I do (and I've tried quite a few things) the actual method setStringValue gets run ... which then generates an Exception as this is a mock.

For info, the failure looks like this:

java.lang.NullPointerException
at org.apache.lucene.document.Field.setStringValue(Field.java:307)
at org.spockframework.mock.runtime.GroovyMockMetaClass.doInvokeMethod(GroovyMockMetaClass.java:86)
at org.spockframework.mock.runtime.GroovyMockMetaClass.invokeMethod(GroovyMockMetaClass.java:42)
at core.LuceneEngine.processLineBatch(lucene_functions.groovy:422)

... where line 422 is the line fileField.setStringValue (...

This seems contrary to what I'd expect with a non-Groovy mock. Can anyone explain what I've got wrong and whether there's a solution?

NB TextField in Lucene 6 is here... from which you can link to superclass Field and see that setStringValue is (non-final) public void.

mike rodent
  • 14,126
  • 11
  • 103
  • 157
  • It is always tedious to make your code snippets runnable. Why not provide full application and test classes with package names and imports? I am at it already, but I remember it also was tedious with you previous question. I don't like to do your job, creating an [MCVE](http://stackoverflow.com/help/mcve) so as to reproduce your problem. You habe 2.6k reputation and should know better. Just parsing your code in my brain is not enough, I need to run it. – kriegaex Mar 17 '18 at 04:15
  • Sorry about that... but you're not under an obligation to answer a question, let alone to make an MCVE. You could just leave a note saying you'll look at things if you make an MCVE. I just thought someone might be able to look at my code and say: "aha, that thing there is wrong"... – mike rodent Mar 17 '18 at 08:07
  • 1
    Of course I help people here voluntarily. Still, the MCVE post here on SO explains the value of it and that the author should provide one so as to make it easier for others to answer the question. I want to help, so I did the work. It does not mean I like it. Feel free so search how many dozens of times I already requested MCVEs from posters. Most of them never even answer. Many others ask follow-up questions or even try to post sample code in comments (unreadable) but are still too lazy to create an MCVE. – kriegaex Mar 17 '18 at 10:23
  • 1
    Personally I really appreciate the efforts you and other experts make and would personally not just ignore a request to make an MCVE: I would indeed regard that as discourteous. – mike rodent Mar 17 '18 at 11:23

1 Answers1

2

Actually you have basically asked the same question already and you have even accepted my answer! You need to use a global Groovy mock:

package de.scrum_master.stackoverflow

import org.apache.lucene.document.TextField
import org.apache.lucene.index.IndexWriter
import org.apache.lucene.index.IndexableField

class LuceneEngine {
  TextField textField
  TextField fileField
  IndexWriter indexWriter
  String currentFilename
  Iterable<? extends IndexableField> singleLDoc

  def processLineBatch(List lineBatch, int deliveryNo) {
    String lDocText = lineBatch.join('\n').trim()
    textField.setStringValue(lDocText)
    fileField.setStringValue(currentFilename)
    indexWriter.addDocument(singleLDoc)
  }
}
package de.scrum_master.stackoverflow

import org.apache.lucene.document.TextField
import org.apache.lucene.index.IndexWriter
import spock.lang.Specification

class LuceneEngineTest extends Specification {
  def "fileField should be set for each line batch"() {
    given:
    LuceneEngine le = new LuceneEngine()
    le.indexWriter = Mock(IndexWriter) {
      addDocument(_) >> null
    }
    le.currentFilename = 'dummy filename'
    // Assign this to le.textField or le.fileField if you like, it does not
    // make a difference because the Groovy mock is GLOBAL
    GroovyMock(TextField, global: true)

    when:
    le.processLineBatch(['dummy text'], 0)

    then:
    1 * le.fileField.setStringValue('dummy filename') >> null
  }
}

As for why you need to use a global mock here, I cannot explain it. This is a question for the Spock mailing list. I have again done your work and posted a question there.

kriegaex
  • 63,017
  • 15
  • 111
  • 202
  • Thanks for answering... but before posting here I had actually tried GroovyMock, with and without global : true. Obviously I had done something bad. I had totally forgotten about that previous question and it didn't come up during my pre-posting research: I'm trying to teach myself Spock (and Groovy) and it's a fairly steep learning curve. I intend to delete this question. You don't have to answer a question, particularly if you realise it's a dupe. Sorry for this mistake on my part. – mike rodent Mar 17 '18 at 08:05
  • I hope you do not delete it after I took the trouble to recreate your situation, write an answer and even post for you on the Spock mailing list. It is not an exact dupe, only the same root cause. And in your previous post you claimed in your own answer that it works for you without global mock, which I think is wrong. – kriegaex Mar 17 '18 at 10:18
  • Yes, that was why I hesitated before deleting. Thanks once again for your great help: it's really appreciated. – mike rodent Mar 17 '18 at 11:25