如何使用@Transactional注释测试方法

Nan*_*noC 6 java testing mongodb spring-data spring-boot

我如何使用 @Transaction 注释测试方法。我编写了一个方法,如果我在应用程序运行时(手动)测试它,该方法就可以工作。但我对此的测试不起作用。我的目标是编写一个测试,确保发生异常时我的事务方法可以正常工作,因此数据库中不会存储任何内容。也许测试Transaction注解是否存在就足够了,相信spring工作得很好。:/但这对我来说很难看。

所以我的目标不是测试 springs transaction 的功能。我的目标是进行一个测试,确保如果发生异常,数据库中不会存储任何内容。也许有更好的方法来测试它。

我每次在测试运行之前都会删除数据库。公司的 id 每次都是随机长的。

环境

  • 爪哇:11
  • 弹簧引导:2.3.1
  • 蒙戈数据库:4.2.7

应用程序.yml

spring:
 data:
    mongodb:
      uri: mongodb://okAdmin:test@mongo_one:27017,mongo_two:27018,mongo_three:27019/?replicaSet=rs0
      database: "ok"
      auto-index-creation: false
Run Code Online (Sandbox Code Playgroud)

Spring的Mongo配置

@Configuration
public class MongoTransactionConfig extends AbstractMongoClientConfiguration {

    @Value("${spring.data.mongodb.database}")
    private String database;
    @Value("${spring.data.mongodb.uri}")
    private String mongo_uri;

    @Bean
    MongoTransactionManager transactionManager(MongoDatabaseFactory mongoDatabaseFactory) {
        return new MongoTransactionManager(mongoDatabaseFactory);
    }

    @Bean
    public MongoClient mongoClient() {
        return MongoClients.create(mongo_uri);
    }

    @Override
    protected String getDatabaseName() {
        return database;
    }
}
Run Code Online (Sandbox Code Playgroud)

方法

@Slf4j
@Service
public class CompanyService {


    public static final int MIN_NUMBER_OF_BRANCHES = 1;
    private final CompanyRepository companyRepository;

    @Autowired
    public CompanyService(@NonNull CompanyRepository companyRepository) {
        this.companyRepository = companyRepository;
    }


    //  TODO : TEST
    @Transactional // (rollbackFor = {StoreException.class}) => NOT WORKING FOR TESTS/NO CATCHING
    public void addBranch(@NonNull Branch branch){
        log.debug("Service: Add branch to company");
        long companyId = branch.getCompanyId();
        companyRepository.addBranch(branch);
        long branches_number = companyRepository.countBranchesForCompany(companyId);
        if (branches_number == MIN_NUMBER_OF_BRANCHES) {
            companyRepository.setSignupCompleted(companyId, true);
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

测试

@SpringBootTest(properties = {
        "spring.data.mongodb.database=ok_test"
})
//@Transactional
class CompanyServiceMongoTest {
    private final String COLLECTION_BRANCHES = "branches";

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private CompanyService testObject;

    @Autowired
    private CompanyRepository companyRepository;


    @Test
    void addBranch_test_transaction_company_not_saved(){
        // PREPARE
        Branch branchTestDummy = BranchTest.createBranchTestDummy();
        Query branchQuery = new Query();
        branchQuery.addCriteria(Criteria.where("company_id").is(branchTestDummy.getCompanyId()));
        // ACTION
        assertThrows(StoreException.class, ()-> testObject.addBranch(branchTestDummy));
        // CHECK
        boolean exists = mongoTemplate.exists(branchQuery, BranchDao.class, COLLECTION_BRANCHES);
        assertFalse(exists);
    }
}

Run Code Online (Sandbox Code Playgroud)

测试结果


org.opentest4j.AssertionFailedError: 
Expected :false
Actual   :true

    at org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:55)
    at org.junit.jupiter.api.AssertFalse.assertFalse(AssertFalse.java:40)
    at org.junit.jupiter.api.AssertFalse.assertFalse(AssertFalse.java:35)
    at org.junit.jupiter.api.Assertions.assertFalse(Assertions.java:210)
    at com.localkoop.server.components.company.core.services.CompanyServiceMongoTest.addBranch_test_transaction_company_not_saved(CompanyServiceMongoTest.java:102)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:567)
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:686)
    at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
    at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
    at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:212)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:208)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:137)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:71)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1507)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1507)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:248)
    at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$5(DefaultLauncher.java:211)
    at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:226)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:199)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:132)
    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:69)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)

Run Code Online (Sandbox Code Playgroud)

运行控制台输出

2020-06-17 11:12:34.635  INFO 182967 --- [mongo_two:27017] org.mongodb.driver.connection            : Opened connection [connectionId{localValue:1426609, serverValue:1511263}] to mongo_two:27017
2020-06-17 11:12:34.635 DEBUG 182967 --- [mongo_two:27017] org.mongodb.driver.cluster               : Checking status of mongo_two:27017
2020-06-17 11:12:34.635 DEBUG 182967 --- [mongo_two:27017] org.mongodb.driver.protocol.command      : Sending command '{"ismaster": 1, "$db": "admin", "$clusterTime": {"clusterTime": {"$timestamp": {"t": 1592385154, "i": 1}}, "signature": {"hash": {"$binary": {"base64": "2Qn1NUq/OPeFI3jd2aPIoloM/x0=", "subType": "00"}}, "keyId": 6837089852169650179}}}' with request id 2853624 to database admin on connection [connectionId{localValue:1426609, serverValue:1511263}] to server mongo_two:27017
2020-06-17 11:12:34.635 DEBUG 182967 --- [mongo_two:27017] org.mongodb.driver.protocol.command      : Execution of command with request id 2853624 completed successfully in 0.32 ms on connection [connectionId{localValue:1426609, serverValue:1511263}] to server mongo_two:27017
2020-06-17 11:12:34.636  INFO 182967 --- [mongo_two:27017] org.mongodb.driver.cluster               : Monitor thread successfully connected to server with description ServerDescription{address=mongo_two:27017, type=REPLICA_SET_PRIMARY, state=CONNECTED, ok=true, minWireVersion=0, maxWireVersion=8, maxDocumentSize=16777216, logicalSessionTimeoutMinutes=30, roundTripTimeNanos=403948, setName='rs0', canonicalAddress=mongo_one:27017, hosts=[mongo_one:27017], passives=[mongo_two:27017, mongo_three:27017], arbiters=[], primary='mongo_one:27017', tagSet=TagSet{[]}, electionId=7fffffff0000000000000009, setVersion=1, lastWriteDate=Wed Jun 17 11:12:34 CEST 2020, lastUpdateTimeNanos=9570708142930}
2020-06-17 11:12:34.636  INFO 182967 --- [mongo_two:27017] org.mongodb.driver.cluster               : Adding discovered server mongo_three:27017 to client view of cluster
2020-06-17 11:12:34.636  INFO 182967 --- [mongo_two:27017] org.mongodb.driver.cluster               : Canonical address mongo_one:27017 does not match server address.  Removing mongo_two:27017 from client view of cluster
2020-06-17 11:12:34.636 DEBUG 182967 --- [mongo_two:27017] org.mongodb.driver.cluster               : Updating cluster description to  {type=REPLICA_SET, servers=[{address=mongo_one:27017, type=REPLICA_SET_PRIMARY, roundTripTime=0.6 ms, state=CONNECTED}, {address=mongo_three:27017, type=UNKNOWN, state=CONNECTING}]
2020-06-17 11:12:34.636 DEBUG 182967 --- [mongo_two:27017] org.mongodb.driver.connection            : Closing connection connectionId{localValue:1426609, serverValue:1511263}
2020-06-17 11:12:34.637  INFO 182967 --- [ngo_three:27017] org.mongodb.driver.connection            : Opened connection [connectionId{localValue:1426610, serverValue:1511264}] to mongo_three:27017
2020-06-17 11:12:34.637 DEBUG 182967 --- [ngo_three:27017] org.mongodb.driver.cluster               : Checking status of mongo_three:27017
2020-06-17 11:12:34.637 DEBUG 182967 --- [ngo_three:27017] org.mongodb.driver.protocol.command      : Sending command '{"ismaster": 1, "$db": "admin", "$clusterTime": {"clusterTime": {"$timestamp": {"t": 1592385154, "i": 1}}, "signature": {"hash": {"$binary": {"base64": "2Qn1NUq/OPeFI3jd2aPIoloM/x0=", "subType": "00"}}, "keyId": 6837089852169650179}}}' with request id 2853626 to database admin on connection [connectionId{localValue:1426610, serverValue:1511264}] to server mongo_three:27017
2020-06-17 11:12:34.637 DEBUG 182967 --- [ngo_three:27017] org.mongodb.driver.protocol.command      : Execution of command with request id 2853626 completed successfully in 0.36 ms on connection [connectionId{localValue:1426610, serverValue:1511264}] to server mongo_three:27017
2020-06-17 11:12:34.637  INFO 182967 --- [ngo_three:27017] org.mongodb.driver.cluster               : Monitor thread successfully connected to server with description ServerDescription{address=mongo_three:27017, type=REPLICA_SET_PRIMARY, state=CONNECTED, ok=true, minWireVersion=0, maxWireVersion=8, maxDocumentSize=16777216, logicalSessionTimeoutMinutes=30, roundTripTimeNanos=457039, setName='rs0', canonicalAddress=mongo_one:27017, hosts=[mongo_one:27017], passives=[mongo_two:27017, mongo_three:27017], arbiters=[], primary='mongo_one:27017', tagSet=TagSet{[]}, electionId=7fffffff0000000000000009, setVersion=1, lastWriteDate=Wed Jun 17 11:12:34 CEST 2020, lastUpdateTimeNanos=9570709659086}
2020-06-17 11:12:34.637  INFO 182967 --- [ngo_three:27017] org.mongodb.driver.cluster               : Adding discovered server mongo_two:27017 to client view of cluster
2020-06-17 11:12:34.637  INFO 182967 --- [ngo_three:27017] org.mongodb.driver.cluster               : Canonical address mongo_one:27017 does not match server address.  Removing mongo_three:27017 from client view of cluster
2020-06-17 11:12:34.637 DEBUG 182967 --- [ngo_three:27017] org.mongodb.driver.cluster               : Updating cluster description to  {type=REPLICA_SET, servers=[{address=mongo_one:27017, type=REPLICA_SET_PRIMARY, roundTripTime=0.6 ms, state=CONNECTED}, {address=mongo_two:27017, type=UNKNOWN, state=CONNECTING}]
2020-06-17 11:12:34.637 DEBUG 182967 --- [ngo_three:27017] org.mongodb.driver.connection            : Closing connection connectionId{localValue:1426610, serverValue:1511264}

Run Code Online (Sandbox Code Playgroud)

感谢您的帮助

Mar*_*ski 3

就我个人而言,我不会编写单元测试来测试其@Transactional是否正常工作。我的原因是,单元测试执行的环境与bean工作的环境不同。我见过太多单元测试工作正常但实际系统却不行的情况。

您可以尝试编写一个针对系统实例运行的集成测试(发送请求、触发功能、查看数据库以查看它是否是您期望看到的)。对我来说更有意义,但也可能更耗时。