如何在Groovy Spock中模拟带有@Autowired注入的构造函数依赖。

huangapple go评论72阅读模式
英文:

How to mock Autowired constructor dependency in groovy spock

问题

我有一个类,我正在编写Groovy测试用例。这个类具有构造函数自动装配,并调用另一个方法来初始化这些字段。

@Service
public class ServiceA {

    private final PrincipalDao principalDao;
    @Autowired
    public ServiceA(final PrincipalDao principalDao){
        this.principalDao=principalDao;
        this.serviceMap = getMap();           
    }
    	
    private Map<> getMap() {
        final List<ClassA> classAList = principalDao.findAll(); //这行返回null
       
    }    	
}

这行代码 final List<ClassA> classAList = principalDao.findAll(); 返回 null,我无法在Groovy中像以下这样模拟它:

principalDao.findAll() >> list

因为它甚至在我的上述行在Groovy测试用例中被调用之前就已经被调用了。

英文:

I have a class for which i am writing groovy test case. This class has constructor autowiring and calls another method to initialise the fields.:

@Service
public class ServiceA {

private final PrincipalDao principalDao;
    @Autowired
    public ServiceA(final PrincipalDao principalDao){
		this.principalDao=principalDao;
        this.serviceMap = getMap();           
    }
	    	
    private Map&lt;&gt; getMap() {
        final List&lt;ClassA&gt; classAList = principalDao.findAll(); //this line returns null
       
    }    	
}

this line final List&lt;ClassA&gt; classAList = principalDao.findAll(); returns null and I cant mock it in groovy like following:

principalDao.findAll() &gt;&gt; list

because its called even before my above line is called in groovy test case

答案1

得分: 1

看不到你的单元测试很难确定问题是什么但我有时会遇到一个常见的误解就是模拟的时机问题

这里是一个样例代码详细说明了我的意思由于你的问题中缺少部分代码我会假设其中的一些部分但我相信这能解释我要表达的观点希望能帮助你找到答案

服务类
```java
@Service
public class ServiceA{

  private final PrincipalDao principalDao;
  private final Map<String, String> serviceMap;

  @Autowired
  public ServiceA(final PrincipalDao principalDao){
    this.principalDao = principalDao;
    this.serviceMap = getMap();
  }

  private Map<String, String> getMap(){
    final HashMap<String, String> stringStringHashMap = new HashMap<String, String>();
    stringStringHashMap.put("hello", principalDao.khello());
    return stringStringHashMap;
  }

  public String printServiceMap() {
    return serviceMap.get("hello");
  }
}
class ServiceATest extends Specification {

  def "some test"() {
    given:
    PrincipalDao principalDao = Mock()
    principalDao.khello() >> "khello" // 这里的关键是:模拟发生在实例化 ServiceA 之前
    ServiceA someService = new ServiceA(principalDao)

    expect:
    "khello" == someService.printServiceMap()
  }
}

这个单元测试会成功通过。关键在于模拟的时机。principalDao.khello() >> "khello" 发生在 new ServiceA(principalDao) 之前。

如果你不需要在单元测试中修改 serviceMap,你也可以这样做,这样你就不需要在每个单元测试中定义它:

class ServiceATest extends Specification {

  @Shared
  PrincipalDao principalDao = Mock()
  ServiceA someService = new ServiceA(principalDao)

  def setupSpec() {
    principalDao.khello() >> "khello"
  }

  def "some test"() {
    expect:
    "khello" == someService.printServiceMap()
  }
}
英文:

Without seeing you unit test, it's hard to tell what the issue is, but a common misconception I run into sometimes is the timing of the mock.

Here's a sample code elaborating what I mean. Since parts of the code missing from your question, going to assume some parts of it, but I'm sure it explains the point I'm making and hopefully should help you out in finding an answer:

Service class:

@Service
public class ServiceA{

  private final PrincipalDao principalDao;
  private final Map&lt;String, String&gt; serviceMap;

  @Autowired
  public ServiceA(final PrincipalDao principalDao){
    this.principalDao = principalDao;
    this.serviceMap = getMap();
  }

  private Map&lt;String, String&gt; getMap(){
    final HashMap&lt;String, String&gt; stringStringHashMap = new HashMap&lt;String, String&gt;();
    stringStringHashMap.put(&quot;hello&quot;, principalDao.khello());
    return stringStringHashMap;
  }

  public String printServiceMap() {
    return serviceMap.get(&quot;hello&quot;);
  }
}
class ServiceATest extends Specification {

  def &quot;some test&quot;() {
    given:
    PrincipalDao principalDao = Mock()
    principalDao.khello() &gt;&gt; &quot;khello&quot; // this is the key here. Mocking is happening before instantiation of ServiceA
    ServiceA someService = new ServiceA(principalDao)

    expect:
    &quot;khello&quot; == someService.printServiceMap()
  }
}

This unit test passes successfully. The point here is the timing of the mocking. principalDao.khello() &gt;&gt; &quot;khello&quot; happens before new ServiceA(principalDao).

If you don't need to modify serviceMap per unit test, you can also do this so you don't have to define it per unit test:

class ServiceATest extends Specification {

  @Shared
  PrincipalDao principalDao = Mock()
  ServiceA someService = new ServiceA(principalDao)

  def setupSpec() {
    principalDao.khello() &gt;&gt; &quot;khello&quot;
  }

  def &quot;some test&quot;() {
    expect:
    &quot;khello&quot; == someService.printServiceMap()
  }
}

huangapple
  • 本文由 发表于 2020年9月9日 19:40:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/63810898.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定