Как протестировать метод с аннотацией @Transactional - PullRequest
1 голос
/ 17 июня 2020

как я могу протестировать метод с аннотацией @Transaction. Я написал метод, который работает, если я тестирую его при запуске приложения (вручную). Но мой тест на это не работает. Моя цель - написать тест, который убедится, что мой метод транзакции работает при возникновении исключения, поэтому в БД ничего не сохраняется. Возможно, этого достаточно, чтобы проверить, существует ли аннотация Transaction, в уверенности, что spring работает нормально. : / Но для меня это некрасиво.

Так что моя цель не в том, чтобы проверить функциональность транзакции пружин. Моя цель - провести тест, чтобы убедиться, что ничего не будет сохранено в базе данных, если произойдет исключение. Может, есть способ лучше это проверить.

Я каждый раз сбрасываю базу данных перед запуском теста. Идентификатор компании - это каждый раз случайное длинное число.

Настройка

  • java: 11
  • springboot: 2.3.1
  • mongodb: 4.2.7

application.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

Mon go конфигурация для пружины

@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;
    }
}

Метод

@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);
        }

    }
}

Тест

@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);
    }
}

Результат теста


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)

Запуск вывода на консоль

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}

СПАСИБО ЗА ПОМОЩЬ

Ответы [ 2 ]

1 голос
/ 17 июня 2020

Лично я бы не стал писать модульный тест для проверки правильности работы @Transactional. Моя причина в том, что среда для выполнения модульного теста не такая же, как среда, в которой работает компонент. Я видел слишком много случаев, когда модульные тесты работают нормально, а реальная система - нет.

Вы Скорее можно попытаться написать интеграционные тесты, которые будут работать с экземпляром системы (отправьте запрос, активируйте функциональность, загляните в БД, чтобы узнать, соответствует ли это тому, что вы ожидаете увидеть). Для меня это имеет больше смысла, но это может занять больше времени.

0 голосов
/ 18 июня 2020

Итак, я нашел решение, которое мне подходит. Я аннотировал свой тестовый класс с помощью

@DataMongoTest(excludeAutoConfiguration = EmbeddedMongoAutoConfiguration.class, includeFilters = @ComponentScan.Filter(Component.class))
@ActiveProfiles("test")
class CompanyServiceMongoTest {}

. Я не знаю, почему аннотация @SpringBootTest не работает. Я думал, что @SpringBootTest - это «суперконтекст» @DataMongoTest, но мои тесты не работают с аннотацией @SpringBootTest. Не забывайте использовать @Transactional в качестве аннотации на уровне тестового класса. Не работает нормально с механизмом транзакций в сервисе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...