Spock failing to intercept method with optional argument(s) - unit-testing

I'm trying to update a groovy unit test and I'm having an issue with the following code:
def "getDatasetRecords() returns PaginatedSearchResults with a set of DatasetRecords from the repo"() {
setup:
def mockRecords = []
def originalResults = Mock(PaginatedSearchResults)
def modelMock = Mock(Model) {
isEmpty() >> false
filter(_ as org.matonto.rdf.api.Resource, _ as IRI, _ as Value, null) >>> it // Also tried (*_), (_, _, _) w&w/o Classes
}
def recordMock = Mock(DatasetRecord)
recordMock.getModel() >> modelMock
7.times { mockRecords << recordMock }
originalResults.getPage() >> mockRecords
originalResults.getPageNumber() >> 1
originalResults.getTotalSize() >> 7
originalResults.getPageSize() >> 10
catalogManagerMock.findRecord(*_) >>> originalResults
expect:
def results = service.getDatasetRecords(new DatasetPaginatedSearchParams(vf))
results.getPage().size() == 7
results.getPageSize() == 10
results.getTotalSize() == 7
results.getPageNumber() == 1
}
When I debug the code: it appears that model.filter is returning null and a NPE is being thrown when isEmpty() is called here:
public Optional<DatasetRecord> getExisting(Resource resource, Model model, ValueFactory valueFactory, ValueConverterRegistry valueConverterRegistry) {
return (model.filter(resource, valueFactory.createIRI(RDF_TYPE_IRI), this.getTypeIRI()).isEmpty()?Optional.empty():Optional.of(new DatasetRecordImpl(resource, model, valueFactory, valueConverterRegistry)));
}
The NPE:
getDatasetRecords() returns PaginatedSearchResults with a set of DatasetRecords from the repo(org.matonto.dataset.impl.SimpleDatasetManagerSpec) Time elapsed: 0.028 sec <<< ERROR!
java.lang.NullPointerException
at org.matonto.dataset.ontology.dataset.DatasetRecordFactory.getExisting(DatasetRecordFactory.java:65)
at org.matonto.rdf.orm.AbstractOrmFactory.getExisting(AbstractOrmFactory.java:159)
at org.matonto.rdf.orm.AbstractOrmFactory.getExisting(AbstractOrmFactory.java:167)
at org.matonto.dataset.pagination.DatasetRecordSearchResults.lambda$new$0(DatasetRecordSearchResults.java:46)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
at org.matonto.dataset.pagination.DatasetRecordSearchResults.<init>(DatasetRecordSearchResults.java:47)
at org.matonto.dataset.impl.SimpleDatasetManager.getDatasetRecords(SimpleDatasetManager.java:155)
at org.matonto.dataset.impl.SimpleDatasetManagerSpec.getDatasetRecords() returns PaginatedSearchResults with a set of DatasetRecords from the repo(SimpleDatasetManagerSpec.groovy:371)
The definition for filter:
Model filter(Resource subject, IRI predicate, Value object, Resource... context);
I have tried fully specking the method signature for the mock object with and without the last parameter. Any help would be appreciated getting the mock interceptor for the filter method working here.

Try this way:
class Spec extends Specification {
def 'mock returns itself'() {
given:
def mock = Mock(Model) {
filter(*_) >> it
}
expect:
mock.filter(1, 2, 3, 4) == mock
mock.filter(1, 2, 3) == mock
}
}
class Model {
Model filter(a, b, c, ... d) {
new Model()
}
}

My code (above) was fine, but a small change was made:
def modelMock = Mock(Model) {
isEmpty() >> false
filter(*_) >> it
}
My problem turned out to be IntelliJ and trying to debug it there.

Related

Unit testing retries with project reactor

I have a piece of code that goes something like this:
somePublisher
.subscribeOn(...)
.flatMap { x -> someFunctionThatReturnsMono(x) }
.retry(3)
.subscribe()
So far I have managed to unit test happy paths, like whether the code inside map {...} is getting called, using the tools from reactor-test.
Now I want to test errors and retries. How can I test to make sure someFunctionThatReturnsMono(x) is getting called at most 4 times when consecutive errors occur?
Simple way is to mock function and count its calls:
def 'Test retry'(){
setup:
someBean = Mock(SomeBean)
def testMono = Mono.just(X)
.flatMap { x -> someBean.someFunctionThatReturnsMono(x) }
.retry(3)
.subscribeOn(Schedulers.elastic())
when:
StepVerifier.create(testMono).verifyError(IllegalArgumentException)
then:
4 * someBean.someFunctionThatReturnsMono(_ as X) >> Mono.error(new IllegalArgumentException())
}
Another one is to mock funtion to return PublisherProbe and count probe subscriptions:
def 'Test retry'() {
when:
PublisherProbe probe = PublisherProbe.of(Mono.error(new IllegalArgumentException()))
someBean = Mock(SomeBean) {
someFunctionThatReturnsMono(_) >> probe
}
def testMono = Mono.just(X)
.flatMap { x -> someBean.someFunctionThatReturnsMono(x) }
.retry(3)
.subscribeOn(Schedulers.elastic())
then:
StepVerifier.create(testMono).verifyError(IllegalArgumentException)
probe.subscribeCount() == 4
}
This is how I do it for one of my use-cases:
def "should retry failed action given number of times"() {
given:
def invocationCounter = new AtomicInteger(0)
when:
Mono<Void> mono = myService.doSth()
.doOnError { invocationCounter.incrementAndGet() }
then:
StepVerifier.create(mono)
.verifyErrorMatches { it == expectedError }
and:
invocationCounter.get() == 1 + retryNum
}

Spock add mock element to a SortedSet

I have a Spy for an instance of a domain class ProjectSegmentCategory, which contains a SortedSet of instances of another domain class ProjectSegmentCategoryValue. As the two domain classes are both quite complex and have a lot of dependencies, I want to mock both as Mock or Spy.
My problem is, that I don't know, how to add a Spy/Mock to a SortedSet. From the error I get, I look like when I want to add the object to the SortedSet, it tries to create a new object with a Copy constructor and this fails, because the original object is a Spy.
So my question is, how can I add a Spock Spy to a Set?
This is my code:
class ProjectSegmentCategoryGsonSpec extends Specification implements JsonViewTest {
// Define the spy objects needed for the test
def projectsegmentcategory = Spy(ProjectSegmentCategory);
def librarycategory = Spy(LibrarySegmentCategory);
def projectsegmentcategoryvalue = Spy(ProjectSegmentCategoryValue)
Set<ProjectSegmentCategoryValue> projectsegmentcategoryvalues;
def projectsegmentclassification = Spy(ProjectSegmentClassification)
def librarycategoryvalue = Spy(LibrarySegmentCategoryValue)
def setup() {
// Set attributes of the objects for the test
projectsegmentcategory.id = 1;
projectsegmentcategory.hasData = true;
projectsegmentcategory.isClustered = true;
projectsegmentcategory.isUnlockedForBudgetingModule = true;
librarycategory.id = 1;
projectsegmentcategory.librarycategory = librarycategory;
def projectsegmentcategoryvalues_list = [projectsegmentcategoryvalue]
projectsegmentcategory.projectsegmentcategoryvalues = new TreeSet<ProjectSegmentCategoryValue>();
projectsegmentcategory.projectsegmentcategoryvalues.add(Mock(ProjetSegmentCategoryValue))
projectsegmentcategory.projectsegmentcategoryvalues.first().id = 1
projectsegmentcategoryvalue.isClustered = true
projectsegmentcategoryvalue.isUnlockedForBudgetingModule = true
librarycategoryvalue.id = 1
projectsegmentcategoryvalue.projectsegmentcategory = projectsegmentcategory
projectsegmentcategoryvalue.librarycategoryvalue = librarycategoryvalue
projectsegmentclassification.id = 1
projectsegmentcategory.projectsegmentclassification = projectsegmentclassification
}
void "Test ProjectSegmentCategory Gson with default values"() {
when:"A ProjectSegmentCategory gson is rendered"
def renderResult = render(template: "/project/instance/projectSegmentCategory/projectSegmentCategory", model:[projectsegmentcategory: projectsegmentcategory])
then:"The json is correct"
// Mock the method getName as the real method is more complicate and requires a Language object
2 * projectsegmentcategory.getName(_ as String) >> "Test"
2 * projectsegmentcategory.getName() >> "Test"
2 * projectsegmentcategory.toString() >> "Test"
2 * projectsegmentcategoryvalue.getName(_ as String) >> "Test"
1 * projectsegmentcategoryvalue.getTranslationByLanguageId(_ as Long) >> "Test"
1 * projectsegmentcategoryvalue.getId() >> "1"
//1 * ProjectSegmentCategoryValue.getTranslationByLanguageId(_ as Long) >> "Test"
1 * projectsegmentcategory.projectsegmentcategoryvalues.iterator(_) >> projectsegmentcategoryvalue.iterator()
// Tests
renderResult.json.id == 1
renderResult.json.isClustered == true
renderResult.json.isUnlockedForBudgetingModule == true
renderResult.json.librarycategoryvalue.id == 1
renderResult.json.projectsegmentcategory.id == 1
renderResult.json.name == "Test"
}
}

How to test a grail service that has resultTransformer within withCriteria(Spock).?

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)

how do I get a groovy MockFor/StubFor ignore method to use a demand method

Below is the behavior I am seeking. I want a Groovy MockFor ignore method to call a demand method, instead of the ignore method calling the dontIgnoreMe() method directly.
Essentially, I want to replace dontIgnoreMe() me with a mock, and have ignoreMe() call the mock (which I have done with metaClass) It appears like categories might be a better solution. I'll look into that next week, see: groovy nabble feed
import groovy.mock.interceptor.MockFor
class Ignorable {
def dontIgnoreMe() { 'baz' }
def ignoreMe() { dontIgnoreMe() }
}
def mock = new MockFor(Ignorable)
mock.ignore('ignoreMe')
mock.demand.dontIgnoreMe { 'hey' }
mock.use {
def p = new Ignorable()
assert p.dontIgnoreMe() == 'hey'
assert p.ignoreMe() == 'hey'
}
Assertion failed:
assert p.ignoreMe() == 'hey'
| | |
| baz false
Ignorable#6879c0f4
For groovy developers I strongly recommend Spock Framework!
Use Spy like in the code below:
def "Testing spy on real object with spock"() {
given:
Ignorable ignorable = Spy(Ignorable)
when:
ignorable.dontIgnoreMe() >> { 'hey' }
then:
ignorable.ignoreMe() == 'hey'
ignorable.dontIgnoreMe() == 'hey'
}

How do you unit test the response status of a Grails controller?

I have the following functions in a controller
def render201 = {
render(status:201)
}
def render202 = {
response.setStatus(202)
}
def render203 = {
response.setStatus(203)
render(status:203)
}
def render204 = {
response.setStatus(204)
render(status:205)
}
And I have the following tests
void test201() {
controller.render201()
assertEquals(201, controller.response.status)
}
void test202() {
controller.render202()
assertEquals(202, controller.response.status)
}
void test203() {
controller.render203()
assertEquals(203, controller.response.status)
}
void test204() {
controller.render204()
assertEquals(204, controller.response.status)
}
test201 fails with this message
junit.framework.AssertionFailedError: expected:<201> but was:<200>
For some reason, if you don't explicitly set the response status, render will always return 200 when being run from a unit test.
Additionally, if I were to actually call these from a browser, render202 would return an error, but render201 and render203 would work just fine. I don't know what render204 would do.
What's going on here? Is this a bug in Grails?
Try something like this :
assertEquals(201, controller.renderArgs.status)
It worked for me.
If you want to understand the inside of mockController, look at :
https://svn.codehaus.org/grails/trunk/grails/src/groovy/grails/test/MockUtils.groovy
clazz.metaClass.getForwardArgs = {-> fwdArgs}
clazz.metaClass.getRedirectArgs ={-> redArgs}
clazz.metaClass.getRenderArgs ={-> renArgs}
clazz.metaClass.forward = {Map map -> forwardArgs.putAll(map)}
clazz.metaClass.redirect = {Map map -> redirectArgs.putAll(map)}
clazz.metaClass.render = {String text -> delegate.response.writer << text}
clazz.metaClass.render = {Converter arg -> delegate.response.writer << arg.toString()}
expected:<201> but was:<200> means you try to request operation which is returning some response. If you want to test 201 need to void method.