如何将mock对象注入构造函数?

时间:2016-07-15 22:52:43

标签: java unit-testing groovy mocking spock

我刚刚开始研究Java应用程序的Spock测试框架。我以前没有Groovy的经验。我们如何使用Spock框架将mock注入构造函数?以下是我的代码和测试示例。

public class ResourceClass {
    private final IDynamoDBMapper factory = new DefaultDynamoDBClientFactory(DynamoDBConfig.fromProperties()).mapperClient();

    private ServiceClass service;

    @Inject
    public ResourceClass(ServiceClass service) {
        this.service = service;
    }
}

我尝试创建如下的测试类。

class ResourceClassTest extends Specification {
ResourceClass eventsResource
ServiceClass service

def setup() {
    service = Mock(ServiceClass)
    eventsResource = new ResourceClass(service)
}

但是我在eventsResource = new ResourceClass(s​​ervice)

时遇到异常
java.lang.NullPointerException: Domain name must be specified.

at java.util.Objects.requireNonNull(Objects.java:228)

有什么建议吗?

2 个答案:

答案 0 :(得分:1)

问题不在于注射'将模拟器放入ResourceClass的构造函数中,因为您只是调用传递模拟的构造函数。发生此异常的原因是因为此字段声明+初始化:

private final IDynamoDBMapper factory = new DefaultDynamoDBClientFactory(DynamoDBConfig.fromProperties()).mapperClient();

工厂字段的初始化将在构造函数执行之前发生(它将在编译期间实际复制到构造函数的开头)。

您可以检查工厂初始化失败的原因(例如检查DynamoDBConfig.fromProperties()如何工作以及应指定dynamo数据库连接的域属性的位置),或者您可以修改源代码以将工厂对象注入到资源类与使用服务的方式相同,通过构造函数,然后在测试中将IDynamoDBMapper的模拟传递给服务构造函数:

public class ResourceClass {
    private final IDynamoDBMapper factory;
    private ServiceClass service;

    @Inject
    public ResourceClass(ServiceClass service, IDynamoDBMapper factory) {
        this.service = service;
        this.factory = factory;
    }
}

class ResourceClassTest extends Specification {
    ResourceClass eventsResource
    ServiceClass service
    IDynamoDBMapper mapper

    def setup() {
        service = Mock(ServiceClass)
        mapper = Mock(IDynamoDBMapper)
        eventsResource = new ResourceClass(service, mapper)
    }
}

使用第二个解决方案,您可以更好地控制对Resource类的测试,但第一个解决方案应该更容易

答案 1 :(得分:1)

要么你需要以某种方式嘲笑你的DefaultDynamoDBClientFactory.mapperClient()或者让它注射,否则它会寻找真正的实例化并且会失败。

public class ResourceClass {
    private IDynamoDBMapper factory;
    private ServiceClass service;

    @Inject
    public ResourceClass(IDynamoDBMapper factory, ServiceClass service) {
        this.factory = factory;
        this.service = service;
    }
}

然后你应该能够以一种奇特的方式对你的resourceClass进行单元测试,如下所示,

class ResourceClassSpec extends Specification {
    ResourceClass eventsResource

    def setup() {
        eventsResource = new ResourceClass(factory: Mock(IDynamoDBMapper), service: Mock(ServiceClass))
    }

    def 'test does something' () {
      given: 'given x'
          //
      when: 'when you call some method of resourceClass'
          //
      then: 'what you expect'
          // 
          1 == 2
    }
}