在单元测试中,运行实际代码而不是函数stub&模拟对象

时间:2016-03-11 15:27:34

标签: java unit-testing amazon-s3 mockito

这是一个非常简单的单元测试用例。

我在班级School中有两种方法:

protected S3Object getAwsObject(AmazonS3Client client, String bucketName, String keyName) {
        GetObjectRequest objRequest =  new GetObjectRequest(bucketName, keyName);
        return client.getObject(objRequest);
}

上面的方法由下面显示的第二种方法调用:

public void doTask() {
   // get client
   AmazonS3Client client = getAwsS3Client();
   // invoke the 1st method
   S3Object s3Obj = getAwsObject(client, "my-bucket", "my-key");
   ...
}

我使用Mockito来测试方法doTask(),我尝试模拟 AmazonS3Client& 存根功能getAwsObject()

@Test
public void testDoTask() {
   // partially mocked School instance
   School school = new School();
   School schoolSpy = Mockito.spy(school);
   // mock the client & s3 object
   AmazonS3Client mockedClient = Mockito.mock(AmazonS3Client.class);

   S3Object mockedS3Obj = Mockito.mock(S3Object.class);

   Mockito.doReturn(mockedClient)
       .when(schoolSpy).getAwsS3Client();

   // PROBLEM HERE: I stub function to return mocked S3Object, but real code is run
   Mockito.doReturn(mockedS3Obj)
          .when(schoolSpy).getAwsObject(mockedClient, "my-bucket", "my-key");

   // system under test
   schoolSpy.doTask();
}

运行测试时,出现以下错误:

com.amazonaws.services.s3.model.AmazonS3Exception: 
The AWS Access Key Id you provided does not exist in our records. 

(Service: Amazon S3; Status Code: 403; Error Code: InvalidAccessKeyId; Request ID: 6B973FC095C28524),...

看起来测试用例运行实际代码client.getObject(objRequest)而不是使用getAwsObject(...)的存根,为什么?

3 个答案:

答案 0 :(得分:3)

如果您没有成功进行模拟工作,可以通过以这种方式覆盖原始方法来解决您的问题:

// mock the client & s3 object
final AmazonS3Client mockedClient = Mockito.mock(AmazonS3Client.class);
final S3Object mockedS3Obj = Mockito.mock(S3Object.class);

School school = new School(){

  @Override
  protected S3Object getAwsObject(AmazonS3Client client, String bucketName, String keyName) {
    return mockedS3Obj;
  }

  @Override
  protected AmazonS3Client getAwsS3Client() {
    return mockedClient;
  }

};

// system under test
school.doTask();

答案 1 :(得分:1)

首先,您不应该在遗留代码中使用间谍。 (http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#spy%28T%29)。如果您在非静态工厂/构建器中委派对象的创建,则很容易进行测试。

但是,在这种情况下,如果mockito执行实际代码,那么doReturn ...无效。我认为Mockito不验证params子句,然后它不会启动doReturn子句。

你有

doReturn...getAwsObject(mockedClient, "my-bucket", "my-key")

如果你做下一个并且它有效,那么我是正确的100%

doReturn...getAwsObject(any(AmazonS3Client.class), anyString(), anyString())

当我在同一声明中使用模拟参数和非模拟参数时,我遇到了问题。试试这个:

getAwsObject(mockedClient, eq("my-bucket"), eq("my-key")); 

如果这不起作用,请尝试使用其他解决方案:

eq(mockedClient), eq(...), eq(...)

最后,如果它不起作用,也许@max解决方案会更容易。

答案 2 :(得分:0)

这里做了多个错误的事情。

  • 不应为被测试的类创建间谍。
  • 为整个班级编写单元测试以测试班级中的行为。如果同一类中的方法是Mocked,那么单元测试的目的是什么?
  • 只应该模拟依赖类,并且可以提供setter方法来设置这些依赖项。这样就可以决定是注入真实对象还是模拟对象。

  • 相关问题