Spring defaultrollback after class - spring

I have an Integration test using TestNG for a DAO class manager by Spring/Hibernate. As shown below. I was trying to have one test insert the data and then other test just to test the fetch method. So I have used dependsOn provided by TestNG. But now the problem is once my first test ends it roll backs the transaction as I have given defaultRollBack = true in TransactionConfiguration annotation. so my second test fails. Is there any way to stop it and rollback after all the tests in class are done?
import static org.testng.Assert.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.testng.annotations.Test;
import com.worksheet.State;
#ContextConfiguration("classpath*:spring-context-test.xml")
#TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
#Transactional(propagation = Propagation.REQUIRES_NEW)
public class StateDAOTest extends AbstractTransactionalTestNGSpringContextTests{
#Autowired
private StateDAO stateDAO;
#Test
public void saveUnknownState(){
State state = new State("Unknown", "UKN");
stateDAO.saveState(state);
assertTrue(state.getId() > 0);
}
#Test(dependsOnMethods = {"saveUnknownState"})
public void fetchUnknownState(){
State state = stateDAO.getStateByAbbreviation("UKN");
assertNotNull(state);
}
}

Related

RestAssured, H2, SpringBootTest Transaction Management. Persisted data not available when calling REST Interface

I wrote a simple SpringBootTest where I tried to read test data from a JSON file and insert it into the database in the #BeforeEach annotated method. When querying the data in the test method, I indeed find the data in the repository. When the REST interface is called via RestAssured and the corresponding method is executed, no data is found via the respository. However, when setting rollback=false in the test, I can find the data in the H2 database. Test code as follows:
package mypackage;
import static io.restassured.RestAssured.get;
import static org.hamcrest.Matchers.hasItems;
import static org.mockito.ArgumentMatchers.eq;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.Objects;
import mypackage.MessageEntity;
import mypackage.MessageRepo;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.restassured.RestAssured;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
#EnableAutoConfiguration
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = { "spring.main.lazy-initialization=true",
"spring.datasource.url=jdbc:h2:file:C:/mydatabase", "spring.jpa.properties.hibernate.default_schema=",
"spring.jpa.hibernate.ddl-auto=create", "spring.datasource.driverClassName=org.h2.Driver",
"spring.jpa.properties.hibernate.show_sql=true", "spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect",
"spring.datasource.username=sa" })
#ActiveProfiles("test")
class MyWebServiceTest{
#Autowired
MessageRepo messageRepo;
#LocalServerPort
int port;
#Autowired
private PlatformTransactionManager platformTransactionManager;
#BeforeEach
void beforeAll() throws IOException, URISyntaxException {
RestAssured.baseURI = "http://localhost/webservice";
RestAssured.port = port;
TransactionTemplate transactionTemplate = new TransactionTemplate(this.platformTransactionManager);
final List<MessageEntity> messages = setupMessages();
transactionTemplate.execute((TransactionCallback<Object>) status -> messageRepo.saveAllAndFlush(messages));
}
#Test
#Transactional
#Rollback(false)
void test() {
System.out.println(messageRepo.findAll()); // successfully retrieves data
get("/messages").then().assertThat().body("$", hasItems(67)).and().statusCode(eq(200)); // in the corresponding method of the WebService, no data is found
}
private List<MessageEntity> setupMessages() throws IOException, URISyntaxException {
final String messagesString = Files.readString(
Paths.get(Objects.requireNonNull(MyWebServiceTest.class.getResource("/messages.json")).toURI()));
return new ObjectMapper().readValue(messagesString, new TypeReference<List<MessageEntity>>() {
});
}
}
I tried to persist the data in the #BeforeEach in different ways, tried flushing etc, but the data is not available when doing messageRepo.findAll() in the method called in the REST-Interface. I would expect the inserted data to be also available there. However, the data is available in the test method, but not at the REST endpoint.
Do you have any idea why this is happening and what I can try to get the desired result with my test data? Thanks!

JUnit 5 exlude tagged test methods within test class

#SpringBootTest
#AutoConfigureMockMvc
#ExcludeTags({"no"})
public class MyClassTest {
#Test
public void test1() {
}
#Test
#Tag("no")
public void test2() {
}
...
}
#RunWith(JUnitPlatform.class)
#SelectClasses({MyClassTest.class})
#IncludeTags({"no"})
public class MyClassTestSuiteTest {
}
Having a Spring Boot 2.3.1 project and testing some REST controllers, in a test class some of the test methods are tagged, and shall not be run, when MyClassTest is run. The annotated methods are run in a test suite (with #IncludeTags("no"). JUnit 5.6.2.
With the test suite I'm not sure it #RunWith has to be used for a test suite, or the JUnit 5 #ExtendWith is the right one? In fact, if not necessary, I don't want to mix JUnit 4 and 5, stick to JUnit 5.
Is there a way to configure simply via annotation or similar, to not run the tagged methods when MyClassTest is run? Like #ExcludeTags for test suites, but this does not work on a class like in the example.
Perhaps two test suites can be created, one with #ExludeTags("no"), one with #IncludeTags("no"). But still, how to prevent then that MyClassTest it run at all?
I don't want to create some Run Configuration in a particular IDE. The preferred way would be to use annotations or similar. Perhaps a Maven configuration would also suffice.
Perhaps on test method level execution of the particular test method can be avoided with some criteria evaluation, if the executed test class is MyClassTest, then don't run that test method.
Interesting here is, I cannot replace #RunWith(JUnitPlatform.class) simply with #ExtendWith(JUnitPlatform.class) as there is type incompatibility. Using #ExtendWith(SpringExtension.class) doesn't give me the possibility to run the class (for example with right-click on the class name, no entry to Run/Debug). But #ExtendWith replaces #RunWith in JUnit 5, what extension to use to run the test suite?
Create Execution Condition ExcludeTagsCondition
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.extension.ConditionEvaluationResult;
import org.junit.jupiter.api.extension.ExecutionCondition;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.platform.commons.util.AnnotationUtils;
public class ExcludeTagsCondition implements ExecutionCondition {
private static final ConditionEvaluationResult ENABLED_IF_EXCLUDE_TAG_IS_INVALID =
ConditionEvaluationResult.enabled(
"#ExcludeTags does not have a valid tag to exclude, all tests will be run");
private static Set<String> tagsThatMustBeIncluded = new HashSet<>();
public static void setMustIncludeTags(final Set<String> tagsThatMustBeIncluded) {
ExcludeTagsCondition.tagsThatMustBeIncluded = new HashSet<>(tagsThatMustBeIncluded);
}
#Override
public ConditionEvaluationResult evaluateExecutionCondition(
ExtensionContext context) {
final AnnotatedElement element = context
.getElement()
.orElseThrow(IllegalStateException::new);
final Optional<Set<String>> tagsToExclude = AnnotationUtils.findAnnotation(
context.getRequiredTestClass(),
ExcludeTags.class
)
.map(a ->
Arrays.asList(a.value())
.stream()
.filter(t -> !tagsThatMustBeIncluded.contains(t))
.collect(Collectors.toSet())
);
if (!tagsToExclude.isPresent() || tagsToExclude.get().stream()
.allMatch(s -> (s == null) || s.trim().isEmpty())) {
return ENABLED_IF_EXCLUDE_TAG_IS_INVALID;
}
final Optional<String> tag = AnnotationUtils.findAnnotation(element, Tag.class)
.map(Tag::value);
if (tagsToExclude.get().contains(tag.map(String::trim).orElse(""))) {
return ConditionEvaluationResult
.disabled(String.format(
"test method \"%s\" has tag \"%s\" which is on the #ExcludeTags list \"[%s]\", test will be skipped",
(element instanceof Method) ? ((Method) element).getName()
: element.getClass().getSimpleName(),
tag.get(),
tagsToExclude.get().stream().collect(Collectors.joining(","))
));
}
return ConditionEvaluationResult.enabled(
String.format(
"test method \"%s\" has tag \"%s\" which is not on the #ExcludeTags list \"[%s]\", test will be run",
(element instanceof Method) ? ((Method) element).getName()
: element.getClass().getSimpleName(),
tag.orElse("<no tag present>"),
tagsToExclude.get().stream().collect(Collectors.joining(","))
));
}
}
Create annotation #ExcludeTags
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import org.junit.jupiter.api.extension.ExtendWith;
#Target({ TYPE, ANNOTATION_TYPE })
#Retention(RUNTIME)
#ExtendWith(ExcludeTagsCondition.class)
public #interface ExcludeTags {
String[] value();
}
On your test
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
#ExcludeTags({"foo", "bar"})
#SpringBootTest
class AppTest {
#Test
#Tag("foo")
void test1() {
System.out.println("test1");
}
#Test
#Tag("bar")
void test2() {
System.out.println("test2");
}
#Test
#Tag("baz")
void test3() {
System.out.println("test3");
}
}
When you run the test, you should see the following output:
test method "test1" has tag "foo" which is on the #ExcludeTags list "[bar,foo]", test will be skipped
test method "test2" has tag "bar" which is on the #ExcludeTags list "[bar,foo]", test will be skipped
test3
And your test runner should show 1 test passing and 2 skipped.
Now for your test suite:
Create an annotation #MustIncludeTags
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
#Target({ TYPE, ANNOTATION_TYPE })
#Retention(RUNTIME)
public #interface MustIncludeTags {
String[] value();
}
Now setup your test suite like so:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Optional;
import java.util.stream.Collectors;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.platform.runner.JUnitPlatform;
import org.junit.platform.suite.api.SelectClasses;
import org.junit.runner.RunWith;
#RunWith(JUnitPlatform.class)
#SelectClasses({MyTestSuite.SetupTests.class, AppTest.class})
#MustIncludeTags({"foo", "bar"})
public class MyTestSuite {
public static class SetupTests {
#BeforeAll
public static void beforeClass() {
ExcludeTagsCondition.setMustIncludeTags(
Optional.ofNullable(MyTestSuite.class.getAnnotation(MustIncludeTags.class))
.map(MustIncludeTags::value)
.map(Arrays::asList)
.orElse(new ArrayList<>())
.stream()
.collect(Collectors.toSet())
);
}
#Disabled
#Test
void testDummy() {
// this test needs to be present for the beforeAll to run
}
}
}
When you run your test suite with the #MustIncludeTags the #ExcludedTags are overridden.
As you can see from the following test execution:

Mockito returns null when mocking Spring Repository

I'm trying to create a test class for some of my services but I haven't been able to mock my DAOs, which are annotated as seen here:
import org.springframework.stereotype.Repository;
#Repository
public class TestDAO {
}
This is my test class
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
#RunWith(MockitoJUnitRunner.class)
class ArchivoTest {
#Mock
public TestDAO testDAO;
#Test
public void test() {
System.out.print(testDAO == null);
}
}
When I run the test with JUnit archivoDAO is always null, and I don't know what to do about it or if I'm missing something. I'm a complete noob at mockito, but a half an hour search through google solved nothing, so I guess it's something not that obvious. Thanks in advance

Unable to inject dependency in Junit test

Having some trouble injecting a dependency in one of my JUnit test classes.
I believe the TestApplication is not package scanning or is not being loaded.
Code below:
package com.mitto.repositories;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
import com.github.springtestdbunit.DbUnitTestExecutionListener;
import com.github.springtestdbunit.annotation.DatabaseSetup;
import com.mitto.MittoApplicationTests;
import com.mitto.domain.User;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration( classes= { MittoApplicationTests.class } )
#TestExecutionListeners({DependencyInjectionTestExecutionListener.class,
TransactionalTestExecutionListener.class,
DbUnitTestExecutionListener.class})
#DatabaseSetup("UserRepositoryTest.xml")
public class UserRepositoryTest {
#Autowired
UserRepository repository;
private static final long FACEBOOK_ID = 1234567;
#Test
public void getUserById() {
User user = repository.findOne(1L);
assertNotNull(user);
assertEquals( user.getFacebookId(), FACEBOOK_ID );
}
}
MittoApplicationTests.java
package com.mitto;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
#SpringBootTest
public class MittoApplicationTests {
#Test
public void contextLoads() {
}
}
UserRepository.java
package com.mitto.repositories;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;
import com.mitto.domain.User;
#Repository
public interface UserRepository extends PagingAndSortingRepository<User, Long>{
User findByFacebookId( long facebookId );
User findByAuthToken( String token );
}
I can't see anything wrong with this.
Sometimes, a working example is better than fixes.
Here is a working example:
First, in your configuration class
#SpringBootApplication
#ComponentScan(value = "com.mitto")
#EnableJpaRepositories(value = "com.mitto")
#EntityScan(basePackages = {"com.mitto.domain"}, basePackageClasses = {Jsr310JpaConverters.class})
public class MittoApplicationTests {
}
Second, in your test class
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = MittoApplicationTests.class) // replace the #ContextConfiguration with #SpringBootTest
// rest of of your annotations ...
public class UserRepositoryTest {
#Autowired
UserRepository repository;
// your test cases
}
A Spring Boot application is just a Spring ApplicationContext, so nothing very special has to be done to test it beyond what you would normally do with a vanilla Spring context. One thing to watch out for though is that the external properties, logging and other features of Spring Boot are only installed in the context by default if you use SpringApplication to create it.
Spring Boot provides a #SpringBootTest annotation which can be used as an alternative to the standard spring-test #ContextConfiguration annotation when you need Spring Boot features. The annotation works by creating the ApplicationContext used in your tests via SpringApplication.
Please read the documentation for more details:
SpringBootTest annotation
boot-features-testing

what is the idiomatic way to use ConfigurationProperties and EnableConfigurationProperties in tests?

i am trying to setup unit tests for some elements to be used within a spring(-boot) application, and i struggled with setup around ConfigurationProperties and EnableConfigurationProperties. the way i finally got it to work doesn't seem consistent with the examples that i have seen in that i have witnessed needing both ConfigurationProperties and EnableConfigurationProperties on my configuration class, which doesn't seem right, and i was hoping that someone might provide some guidance.
here is a simplified example:
JavaTestConfiguration.java
package com.kerz;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.validation.constraints.NotNull;
#Configuration
#ConfigurationProperties
#EnableConfigurationProperties
public class JavaTestConfiguration {
public void setFoo(String foo) {
this.foo = foo;
}
#NotNull
String foo;
#Bean
String foo() {
return foo;
}
}
JavaTestConfigurationTest.java
package com.kerz;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.assertEquals;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {JavaTestConfiguration.class})
#TestPropertySource("classpath:test.properties")
public class JavaTestConfigurationTest {
#Autowired
String foo;
#Test
public void shouldWork() throws Exception {
assertEquals("foo", "bar", foo);
}
}
test.properties
foo=bar
Your test is more integration test if you are starting Spring context. Therefore you should test also production spring configuration.
I would advise not to create testing configuration. Use one production configuration for testing.
You are also using #TestPropertySource annotation, which is used when you need to define test specific properties. If you can test with PROD configuration do not use it.

Resources