如何使用Mockito编写Amazon S3文件上传功能的单元测试?
用Mockito编写Amazon S3文件上传功能的单元测试指导
看起来你已经迈出了第一步,Mock了AmazonS3Client,接下来咱们把这个测试补全,确保能覆盖成功和失败两种核心场景。
首先先明确你的业务代码(我调整了catch块的格式让日志逻辑更清晰):
def insertingFilesInS3(path: String, file: File): Boolean = { try { s3client.putObject(BUCKET_NAME, path, file) true } catch { case ex: Exception => info(s"File storage failed for $path $file") ex.printStackTrace() false } }
下面是完整的单元测试代码示例,我用ScalaTest配合Mockito来实现(这是Scala生态里最常用的测试组合):
import org.scalatest.BeforeAndAfterEach import org.scalatest.funsuite.AnyFunSuite import org.mockito.Mockito._ import org.mockito.ArgumentMatchers._ import com.amazonaws.services.s3.AmazonS3Client import java.io.File // 替换成你实际的业务类名 class S3UploadServiceTest extends AnyFunSuite with BeforeAndAfterEach { // 声明mock对象和业务类实例 private var mockS3Client: AmazonS3Client = _ private var s3UploadService: YourS3BusinessClass = _ // 测试用常量(建议把业务代码里的BUCKET_NAME改成可注入参数,避免硬编码) private val TEST_BUCKET = "test-demo-bucket" private val TEST_FILE_PATH = "documents/report.pdf" private val TEST_FILE = new File("src/test/resources/test-sample.pdf") // 每个测试前初始化mock和业务类,避免测试间状态污染 override def beforeEach(): Unit = { mockS3Client = mock(classOf[AmazonS3Client]) // 把mock的S3客户端注入业务类(假设业务类通过构造器接收依赖) s3UploadService = new YourS3BusinessClass(mockS3Client, TEST_BUCKET) } test("insertingFilesInS3 should return true when upload succeeds") { // 1. 模拟成功场景:调用putObject不抛出异常 doNothing().when(mockS3Client).putObject(eq(TEST_BUCKET), eq(TEST_FILE_PATH), eq(TEST_FILE)) // 2. 执行待测试的方法 val uploadResult = s3UploadService.insertingFilesInS3(TEST_FILE_PATH, TEST_FILE) // 3. 验证结果和交互行为 assert(uploadResult === true) // 确认S3客户端的putObject方法被精确调用了1次,参数完全匹配 verify(mockS3Client, times(1)).putObject(eq(TEST_BUCKET), eq(TEST_FILE_PATH), eq(TEST_FILE)) } test("insertingFilesInS3 should return false and log error when upload fails") { // 1. 模拟失败场景:调用putObject抛出指定异常 val testUploadException = new RuntimeException("S3 connection timeout") doThrow(testUploadException).when(mockS3Client).putObject(eq(TEST_BUCKET), eq(TEST_FILE_PATH), eq(TEST_FILE)) // 2. 执行待测试的方法 val uploadResult = s3UploadService.insertingFilesInS3(TEST_FILE_PATH, TEST_FILE) // 3. 验证结果和交互行为 assert(uploadResult === false) verify(mockS3Client, times(1)).putObject(eq(TEST_BUCKET), eq(TEST_FILE_PATH), eq(TEST_FILE)) // 可选:如果需要验证错误日志是否正确输出,可以用日志框架的测试工具(比如Logback内存Appender)捕获日志并断言 } }
几个关键注意点
- 参数匹配:用
eq()来精确匹配参数,确保测试的是预期的bucket、路径和文件;如果不需要精确匹配,也可以用anyString()、any(classOf[File])这类通配匹配器 - 场景覆盖:必须覆盖成功和失败两种分支,确保业务逻辑的每个代码路径都被测试到
- 交互验证:用
verify()确认mock对象的方法确实被调用,且调用次数符合预期,避免出现“代码没执行到关键逻辑但测试通过”的假阳性情况 - 依赖注入:建议把业务代码里的
BUCKET_NAME和s3client改成可注入的参数,不要硬编码,这样测试时可以轻松替换成测试用的配置,避免影响生产环境
内容的提问来源于stack exchange,提问作者jyoti
相关产品推荐
相关产品推荐

