Spock add mock element to a SortedSet - unit-testing

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"
}
}

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 failing to intercept method with optional argument(s)

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.

Failing Unit Test In Grails 1.3.7 Spock

I have this piece of code in a controller:
def update = {
Map model = [:]
model.foo = params.foo
model.bar = params.bar
def result = ""
MyObject obj = MyObject.findWhere(bar:bar, foo:foo)
MyObjectService.updateObj(model,obj)
result = true
render result as JSON
}
And this simple unit test:
def 'controller update'() {
given:
controller.params.foo = foo
controller.params.bar = bar
MyObject obj = new MyObject(bar:bar, foo:foo)
mockDomain(MyObject,[obj])
when:
controller.update()
then:
1 * MyObject.findWhere(bar:bar, foo:foo) >> obj
1 * MyObjectService.updateObj(model,obj)
and:
def model = JSON.parse(controller.response.contentAsString)
model == true
where:
foo = "0"
bar = "1"
}
Now this is failing by and it is telling me that, "not static method findWhere is applicable..." for those arguments. That "MyObject" is just an orm class, and when I run that application everything seems to be working fine, but the test is failing.
My logic is this:
I want to count how many times the findWhere and updateObj methods are call and I am also mocking their response. So findWhere will return the object I already mocked, and pass it on to the service.
Any ideas why this is failing ?
For mocking static methods you should use Spock's GroovyStub class which introduced in v0.7.

Testing a service in a domain class

I am trying to write a unit test for one of my domain classes right now. I cheated, I know the code works by use of the code however... I want to make sure I have some automated tests in place.
I am getting the following error:
Test a sponsor(some.vendor.Vendor2Spec)
|
java.lang.NullPointerException: Cannot invoke method getLevel() on null object
at some.vendor.Vendor.getSponsorLevel(Vendor.groovy:111)
at some.vendor.Vendor2Spec.Test a sponsor(Vendor2Spec.groovy:29)
|Completed 1 unit test, 1 failed in 0m 3s
.................Tests FAILED
In the following code I have indicated my line numbers where they are being called out as errors.
My Vendor looks like:
class Vendor {
def sponsorService
SponsorLevel getSponsorLevel(){
return sponsorService.getLevel(this) // Line 111
}
}
And my test is setup as follows:
#TestFor(Vendor)
#TestMixin(GrailsUnitTestMixin)
class Vendor2Spec extends Specification{
#Shared
def sponsorService = new SponsorService()
def setup() {
}
def cleanup() {
}
void "Test a sponsor"(){
when: 'A Sponsor donates $5'
def vendor = new Vendor(cashDonation: 5, sponsorService: sponsorService)
then: 'Amount Owed should be $5'
vendor.getAmountDue().equals(new BigDecimal("5"))
vendor.getSponsorLevel() == SponsorLevel.DIAMOND // Line 29
when:"A Sponsor donates an item of value"
vendor = vendor = new Vendor(itemValue: 5)
then: 'Amount Due is $0'
vendor.getAmountDue().equals(new BigDecimal("0"))
vendor.sponsorLevel == SponsorLevel.DIAMOND
}
}
When I started out I was not newing off my sponsorService and it kept complaining about the nullness so... I tried mocking (might have been doing it wrong) but... I need to test the objects use of the service so... I don't think I need a mock.
The service looks like:
class SponsorService {
static transactional = false
def getLevel(Vendor vendor){
if(!vendor){
return null
}
BigDecimal sponsoringAmount = BigDecimal.ZERO
sponsoringAmount = sponsoringAmount.add(vendor.cashDonation ?: BigDecimal.ZERO)
sponsoringAmount = sponsoringAmount.add(vendor.itemValue ?: BigDecimal.ZERO)
return SponsorLevel.getSponsoringLevel(sponsoringAmount)
}
}
And the enum:
enum SponsorLevel {
PLATINUM("Platinum"),
GOLD("Gold"),
SILVER("Silver"),
BRONZE("Bronze"),
DIAMOND("Diamond")
final String label
private SponsorLevel(String label){
this.label = label
}
public static SponsorLevel getSponsoringLevel(BigDecimal sponsoringAmount){
if(!sponsoringAmount){
return null
}
if(sponsoringAmount.compareTo(new BigDecimal("3000")) >= 0){
return PLATINUM
}
if(sponsoringAmount.compareTo(new BigDecimal("2000")) >= 0){
return GOLD
}
if(sponsoringAmount.compareTo(new BigDecimal("1000")) >= 0){
return SILVER
}
if(sponsoringAmount.compareTo(new BigDecimal("500")) >= 0){
return BRONZE
}
if(sponsoringAmount.compareTo(new BigDecimal("1")) >= 0){
return DIAMOND
}
return null
}
}
Generally speaking, Service classes should be mocked when called from other classes for Unit Tests, otherwise, you'd want to write an Integration test.
Personally, if that is all your service is doing I'd just make that a method on the domain itself:
class Vendor {
def sponsorService
SponsorLevel getSponsorLevel(){
BigDecimal sponsoringAmount = BigDecimal.ZERO
sponsoringAmount = sponsoringAmount.add(this.cashDonation ?: BigDecimal.ZERO)
sponsoringAmount = sponsoringAmount.add(this.itemValue ?: BigDecimal.ZERO)
return SponsorLevel.getSponsoringLevel(sponsoringAmount)
}
}
Your service isn't transactional, your getLevel() isn't doing anything with the database, and the method is specific to your Vendor Domain. So to me, it makes more sense to make this a Domain method on Vendor. It simplifies your code and your test.

how to generate fake data using moq for unit test?

I need to generate some data to unit test my repositories. i was using a loop to generate a list of objects, see codes below. I learned moq is a great mocking library, Can I use moq to generate that and how do I do it?
public IQueryable<Category> GetCategories()
{
IList<Category> result = new List<Category>();
for (int i = 1; i <= 2; i++)
{
Category c = new Category();
c.ID = i;
c.Name = "Parent" + i.ToString();
c.ParentID = 0;
for (int x = i*10; x < i*10+5; x++)
{
Category sub = new Category();
sub.ID = x;
sub.Name = "Sub" + x.ToString();
sub.ParentID = i;
result.Add(sub);
}
result.Add(c);
}
return result.AsQueryable<Category>();
}
You can't use Moq to create the data, but you can use AutoFixture:
public IQueryable<Category> GetCategories()
{
return fixture.CreateMany<Category>().AsQueryable();
}
However, this will not give you a hierarchical tree. It will return objects like this:
Object 1:
- ID = 0
- ParentID = 1
Object 2:
- ID = 2
- ParentID = 3
etc.
If you really need to have this hierarchical data, you would need to use the following code:
public IQueryable<Category> GetCategories()
{
var result = new List<Category>();
// Create the parents
var parents = fixture.Build<Category>()
.Without(x => x.ParentID)
.CreateMany());
result.AddRange(parents);
result.AddRange(parents.SelectMany(p => fixture.Build<Category>()
.With(x => x.ParentID, p.ID)
.CreateMany()));
return result.AsQueryable();
}
This will add multiple parents with multiple subs for each parent.
You can use faker.net to generate fake data. for Example: for dotnet core project its Faker.NETCore.
dotnet add package Faker.NETCore -v 1.0.1
and then use the same in your code in the following manner:-
public void GetStudent()
{
var st = new Student()
st.FirstName = Faker.Name.First();
st.LastName = Faker.Name.Last();
st.Mobile = Faker.Phone.Number();
}