Spock Mock not returning any data

49 Views Asked by At

I have a class which adds features to categories of products in the same category and prices based on the productCode of the product. I have a List of List of products in the same category and productCode for which the same price and category should be added. However, when I create a mock for the ProductDecorator it fails to return any products. I am using Lombok's @EqualsAndHashCode.Include on productCode in both classes

Here is the code

public class ProductDetail {
  String productCode;
  String category;
  Double price;

  public String getProductCode() {
    return productCode;
  }
}
class Product {
  String name;
  String productCode;
  String category;
  Double price;

  String getProductCode() {
    return productCode;
  }

  public Product addDetail(String category, Double price) {
    this.category = category;
    this.price = price;
    return this;
  }
}
public class ProductDecorator {
  List<Product> addProductFeature(List<Product> products, List<ProductDetail> details) {
    List<Product> productList = new ArrayList<>();
    products.forEach(product -> {
      details.forEach(detail -> {
        if (product.getProductCode().equals(detail.getProductCode())) {
          Product p = product.addDetail(detail.category, detail.price);
          productList.add(p);
        }
      });
    });
    return productList;
  }
}
  // 3 Products and 3 product details objects that match
  List<List<Product>> products = getProducts()
  List<ProductDetail> productDetailList = getProductDetails()

  ProductDecorator productDecorator = Mock()
  // Returns null all the time for all, just showing the first one
  productDecorator.addProductFeature(products[0],productDetailList)>>[products[0]]  

How do I successfully mock the decorator to match. I think it is not matching because of some equality issues.I tried using underscores but that did not work either. I'd be grateful for a solution to thsi problem

1

There are 1 best solutions below

2
kriegaex On BEST ANSWER

For me, it works like this, so probably your original code looks different:

package de.scrum_master.stackoverflow.q77412076;

import lombok.EqualsAndHashCode;
import lombok.ToString;

@EqualsAndHashCode
@ToString
public class ProductDetail {
  String productCode;
  String category;
  Double price;

  String getProductCode() {
    return productCode;
  }
}
package de.scrum_master.stackoverflow.q77412076;

import lombok.EqualsAndHashCode;
import lombok.ToString;

@EqualsAndHashCode
@ToString
public class Product {
  String name;
  String productCode;
  String category;
  Double price;

  String getProductCode() {
    return productCode;
  }

  Product addDetail(String category, Double price) {
    this.category = category;
    this.price = price;
    return this;
  }
}
package de.scrum_master.stackoverflow.q77412076;

import java.util.ArrayList;
import java.util.List;

public class ProductDecorator {
  List<Product> addProductFeature(List<Product> products, List<ProductDetail> details) {
    List<Product> productList = new ArrayList<>();
    products.forEach(product -> {
      details.forEach(detail -> {
        if (product.getProductCode().equals(detail.getProductCode())) {
          Product p = product.addDetail(detail.category, detail.price);
          productList.add(p);
        }
      });
    });
    return productList;
  }
}
package de.scrum_master.stackoverflow.q77412076

import spock.lang.Specification

class ProductDetailTest extends Specification {
  def 'test without mock'() {
    expect:
    new ProductDecorator().addProductFeature(products[0], productDetails) == [
      new Product(name: 'One', productCode: 'XY-1', category: 'vegetables', price: 12.34),
      new Product(name: 'Two', productCode: 'AB-2', category: 'flowers', price: 23.45),
      new Product(name: 'Three', productCode: 'QR-3', category: 'vegetables', price: 34.56)
    ]
    new ProductDecorator().addProductFeature(products[1], productDetails) == [
      new Product(name: 'Four', productCode: 'XY-1', category: 'vegetables', price: 12.34),
      new Product(name: 'Five', productCode: 'AB-2', category: 'flowers', price: 23.45),
      new Product(name: 'Six', productCode: 'QR-3', category: 'vegetables', price: 34.56)
    ]
  }

  def 'test with mock'() {
    given:
    ProductDecorator productDecorator = Mock()
    productDecorator.addProductFeature(products[0], productDetails) >> products[0]

    expect:
    productDecorator.addProductFeature(products[0], productDetails) == products[0]
    productDecorator.addProductFeature(products[1], productDetails) == null
  }

  List<List<Product>> getProducts() {
    [
      [
        new Product(name: 'One', productCode: 'XY-1'),
        new Product(name: 'Two', productCode: 'AB-2'),
        new Product(name: 'Three', productCode: 'QR-3')
      ],
      [
        new Product(name: 'Four', productCode: 'XY-1'),
        new Product(name: 'Five', productCode: 'AB-2'),
        new Product(name: 'Six', productCode: 'QR-3')
      ]
    ]
  }

  List<ProductDetail> getProductDetails() {
    [
      new ProductDetail(productCode: 'XY-1', category: 'vegetables', price: 12.34),
      new ProductDetail(productCode: 'AB-2', category: 'flowers', price: 23.45),
      new ProductDetail(productCode: 'QR-3', category: 'vegetables', price: 34.56)
    ]
  }
}

Please note: The mock should return products[0] rather than [products[0]], because ProductDecorator.addProductFeature returns List<Product>, not List<List<Product>>. But that is not the root cause of your problem.


Try it in the Groovy Web Console.

There, I used the Groovy equivalent of your Lombok annotations for simplicity's sake. @Canonical is like a combination of @ToString, @EqualsAndHashCode and @TupleConstructor. While recreating your situation, I was also printing values, hence the wish to have nice toString() methods. I also converted your lambdas into Groovy closures. Lambdas are fine in Groovy 4, but closures also work in older Groovy versions.