I have a SpringBoot multimodule application, something like that:
core
customer1 -> depends on core
customer2 -> depends on core
I want to write integration tests for both, but I don't want to duplicate my core test code. Now I have an abstract class with SpringBootTest(classes = Customer1Application.class) and a lot of test classes, mostly testing the core functionality.
#ContextConfiguration
#SpringBootTest(classes = Customer1Application.class)
#AutoConfigureMockMvc
public abstract class AbstractSpringBootTest
{
#Autowired
protected MockMvc mockMvc;
#Autowired
protected Validator validator;
...
}
I want to check if the changes in Customer2 application break something in core functionality, so I want to run these tests with #SpringBootTest(classes = Customer2Application.class) annotation.
How is it possible to configure the application class in the annotation? Is there a way to run the tests with my other application context without manually changing the annotation or duplicating all the steps?
I don't know if it will work, but I would try removing #SpringBootTest from AbstractSpringBootTest and then defining two test classes as follows:
#SpringBootTest(classes = Customer1Application.class)
class Customer1ApplicationSpringBootTest extends AbstractSpringBootTest {}
#SpringBootTest(classes = Customer2Application.class)
class Customer2ApplicationSpringBootTest extends AbstractSpringBootTest {}
EDIT:
So I dug around Spring Boot sources and came up with this solution.
Essentially to be able to use system property or property file to configure which #SpringBootApplication is supposed to be tested you need to copy the source of class org.springframework.boot.test.context.SpringBootConfigurationFinder to your own test source root and the edit method private Class<?> scanPackage(String source) to look something like this (you do not have to use Lombok of course):
private Class<?> scanPackage(String source) {
while (!source.isEmpty()) {
val components = this.scanner.findCandidateComponents(source);
val testConfig = System.getProperties();
val testConfigFile = "test-config.properties";
val applicationClassConfigKey = "main.application.class";
try {
testConfig.load(this.getClass().getResourceAsStream("/" + testConfigFile));
} catch (IOException e) {
logger.error("Error reading configuration file: {}, using default algorithm", testConfigFile);
}
if (testConfig.containsKey(applicationClassConfigKey)) {
if (!components.isEmpty() && testConfig.containsKey(applicationClassConfigKey) && testConfig.getProperty(applicationClassConfigKey) != null) {
boolean found = false;
val configClassName = testConfig.getProperty(applicationClassConfigKey);
for (BeanDefinition component: components) {
if (configClassName.equals(component.getBeanClassName())) {
found = true;
break;
}
}
Assert.state(found,
() -> "Found multiple #SpringBootConfiguration annotated classes "
+ components + ", none of which are of type " + configClassName);
return ClassUtils.resolveClassName(
configClassName, null);
}
} else {
if (!components.isEmpty()) {
Assert.state(components.size() == 1,
() -> "Found multiple #SpringBootConfiguration annotated classes "
+ components);
return ClassUtils.resolveClassName(
components.iterator().next().getBeanClassName(), null);
}
}
source = getParentPackage(source);
}
return null;
}
Check the link for the entire project.
Did you check?
#SpringBootTest(classes = {Customer1Application.class, Customer2Application.class})
Related
I have a class of jUnit 5 tests that is not allowed to run in the main pipeline (for multiple reasons). In order to disabled those tests in the pipeline but work on a developer machine I introduced #DisabledIfEnvironmentVariable for the test class (and it works great):
#DisabledIfEnvironmentVariable(named = "USER", matches = "(.*jenkins.*|.*tomcat.*)")
#SpringBootTest(classes = {BigApplication.class}, webEnvironment = RANDOM_PORT)
class LongRunningApplicationTest { ... }
How can I override #DisabledIfEnvironmentVariable if I want to run the test class on an occasion?
I tried adding #EnabledIfEnvironmentVariable hoping that it will override #DisabledIfEnvironmentVariable annotation, therefore providing me with a convenient way to run the test in the pipeline on occasion:
#EnabledIfEnvironmentVariable(named = "applicationTest", matches = "true")
#DisabledIfEnvironmentVariable(named = "USER", matches = "(.*jenkins.*|.*tomcat.*)")
#SpringBootTest(classes = {BigApplication.class}, webEnvironment = RANDOM_PORT)
class LongRunningApplicationTest { ... }
However the above approach doesn't work. Is there a way to override #DisabledIf... ?
One solution is to introduce your own conditions using following annotations:
#EnabledIf or #DisabledIf.
#EnabledIf("EnabledIfAnnotationUtils#shouldRun")
class ApplicationTest {
#Test
void renameMe() {
assertThat(false).isTrue();
}
}
Where EnabledIfAnnotationUtils - is external class (in case you have multiple tests under same condition) and #shouldRun - name of static method. Example:
public class EnabledIfAnnotationUtils {
static boolean shouldRun() {
boolean override = getPropertySafely("run-long-tests").equalsIgnoreCase("true");
if(override) return true;
String user = getEnvSafely("USER");
boolean isOnJenkins = user.toLowerCase().contains("jenkins") || user.toLowerCase().contains("tomcat");
return !isOnJenkins;
}
private static String getPropertySafely(String name) {
return "" + System.getProperty(name);
}
private static String getEnvSafely(String name) {
return "" + System.getenv(name);
}
}
Now tests will NOT run on Jenkins unless override parameter passed, example:
mvn test -Drun-long-tests=true
I am confused a bit,based on this documentation, I should be only putting this annotation #DataJpaTest and it will configure my application context to be only for JPA repositories and that too for an in-memory database. Now the problem is in my application I have a Main class which is annotated with #SpringBootApplication and it is loading rest of the configuration for Web interceptor and many other things from there. In fact it is trying to load the bootstrap.properties file too.
In my understanding it should NOT be using this configuration.
Below is my test code and main class.
#OpenAPIDefinition(info = #Info(title = "Test API", version = "0.1.10-SNAPSHOT", description = "Test API", contact = #Contact(name = "My Team", email = "sample#mycompany.com")), servers = {
#Server(description = "Current Stage Server url", url = "https://mycompany.com/testapics"),
#Server(description = "Stable Stage Server url", url = "https://mycompany.com/testapi") })
#SpringBootApplication(scanBasePackages = { "com.mycompany.utility", "com.mycompany.sample.users",
"com.mycompany.sample.utility", "com.mycompany.another.sample.interceptor",
"com.mycompany.sample.security.vulnerableinput", "com.mycompany.sample.security.nocache.filter",
"com.mycompany.sample.security.common", "com.mycompany.sample.security.csrf",
"com.mycompany.sample.security.nocache" })
#EnableCaching
#EnableAsync
public class SampleApiApplication implements WebMvcConfigurer {
The main class has a bunch of other bean configurations in it. (Omitting those for clarity).
Below is my test class.
#DataJpaTest
public class UsersRepositoryTest {
#Autowired
private UsersRepository usersRepository;
#Test
public void test_usersRepository_findByEmplId() {
List<User> users = getUsersData();
UsersRepository.saveAll(users);
for (User user : users) {
Assert.assertTrue(user.getId() != null);
}
}
private List<User> getUsersData() {
List<User> userList = new ArrayList<>();
User user = new User();
user.setEmpId("XYZ_001");
user.setUserUUID(UUID.randomUUID().toString());
userList.add(user);
return userList;
}
}
According to the Spring Boot Reference:
If you use a test annotation to test a more specific slice of your application, you should avoid adding configuration settings that are specific to a particular area on the main method’s application class.
The underlying component scan configuration of #SpringBootApplication defines exclude filters that are used to make sure slicing works as expected. If you are using an explicit #ComponentScan directive on your #SpringBootApplication-annotated class, be aware that those filters will be disabled. If you are using slicing, you should define them again.
So in your case consider to move annotations like #EnableCaching, #EnableAsync to a separate class.
#StreamListener are deprecated now, so I try to rewrite my consumer in functional way.
Java code
#Component
public class MyConsumer {
public void handleMyMethod(MessageEntity message) {
...
}
}
#RequiredArgsConstructor
#Configuration
public class MyConsumerConfiguration {
private final MyConsumer myConsumer;
#Bean
public Consumer<MessageEntity> myMethod() {
return myConsumer::handleMyMethod;
}
}
application.yaml
spring.cloud :
stream :
kafka.binder.brokers :
- "127.0.0.1:9092"
function.definition : myMethod
bindings :
myMethod-in-0 :
destination : service.method.update
Test
#Test
void handleMyMethod() {
MessageEntity message = new MessageEntity();
template.send("service.method.update", message);
await()
.atMost(6, TimeUnit.SECONDS)
.untilAsserted(() -> {
verify(myConsumer, atLeastOnce()).handleMyMethod(entityCaptor.capture());
MessageEntity entity = entityCaptor.getValue();
assertNotNull(entity);
});
}
Run test. The message is coming to Kafka (I see it in Kafka Tool), but MyConsumer doesn't catch it. Test fails with the error:
myConsumer.handleMethod(
<Capturing argument>
);
-> at com.my.project.MyConsumer.handleMethod(MyConsumer.java:33)
Actually, there were zero interactions with this mock.```
I found the problem. My util module, integrated in the module I described above, has its own "function.definition" property. And since the module above is configuring before the util module, the util module erases methods from the top-level module. I solved the problem in this way:
#Autowired
StreamFunctionProperties streamFunctionProperties;
...
String definitionString = StringUtils.hasText(streamFunctionProperties.getDefinition()) ?
streamFunctionProperties.getDefinition() + ";" :
"";
streamFunctionProperties.setDefinition(definitionString + "methodDeleteOut;methodUpdateOut;roleUpdateIn;roleDeleteIn");
Wiremock Documentation states that the location of the file specified in withBodyFile should be in src/test/resources/__files. I would like to have file in src/test/resources/Testing_ABC/Testcase2/myfile.xml.
Is there any way I can achieve this ? I tried following, but it does not seem to work !
stubFor(get(urlPathEqualTo("/abc")).willReturn
(aResponse().withHeader("Content-Type",
"text/xml; charset=utf-8").withHeader
("Content-Encoding",
"gzip")
.withBodyFile
("src/test/resources/Testing_ABC/Testcase2/myfile.xml)));
However, when I put my file in src/test/resources/__files/myfile.xml and change the path accordingly, it works fine.
I am just wondering if I can make wiremock look in some other directory in resources other than __files just in order to have nice resource structure in project.
I'm using this Kotlin Config class to customize the Root Directory.
It still requires response file to be in the __files directory.
#Configuration
class Config: WireMockConfigurationCustomizer {
override fun customize(config: WireMockConfiguration?) {
config!!.withRootDirectory("customer-client/src/test/resources")
}
}
#AutoConfigureWireMock
For Java & Spock but still using __files folder
#TestConfiguration
public class WireMockConfig {
#Bean
public WireMockConfigurationCustomizer wireMockConfigurationCustomizer() {
return config -> {
config.withRootDirectory("src/integration-test/resources");
};
}
}
Then where ever you have ApplicationContext initializer :
#SpringBootTest(classes = [Application.class, WireMockConfig.class], webEnvironment = RANDOM_PORT)
#ContextConfiguration()
abstract class WireMockIntegrationSpec extends Specification {
}
Then in your test :
#AutoConfigureWireMock(port = 9089)
#Unroll
class ApplicationSpec extends WireMockIntegrationSpec {
def "Some test" () {
}
}
Your resources will be served from src/integration-test/resources/__files.
Make sure this directory structure exists
This seems to be something you need to configure when creating the rule. Try
#Rule
public final WireMockRule rule = new WireMockRule(WireMockConfiguration.wireMockConfig()
.withRootDirectory("src/test/resources/Testing_ABC"));
and then you probably can use .withBodyFile("Testcase2/myfile.xml) in test.
Hi I was wondering if it's possible to leverage Spring annotated Caching within Scala. I have tried but am receiving the error below. I am running the application from a java package that depends on the scala package.
No cache could be resolved for 'CacheableOperation[public scala.collection.immutable.List MerchantDataGateway.getAllMerchants()]
My Configuration Class
#Configuration
#EnableCaching
#ComponentScan(basePackages = "xxx.xxx.xxx.xxx")
public class EnvironmentHelperConfig {
#Bean
public CacheManager getCacheManager() {
final int ttl = 12;
final int maxCacheSize = 1012;
GuavaCacheManager result = new GuavaCacheManager();
result.setCacheBuilder(CacheBuilder
.newBuilder()
.expireAfterWrite(ttl, TimeUnit.HOURS)
.maximumSize(maxCacheSize));
return result;
}
}
My Scala Class
#Component
class MerchantDataGateway {
#Autowired
var fmcsProxy: MerchantResource = null;
#Cacheable
def getAllMerchants(): List[MerchantViewModel] = {
val merchants = getAllMerchantsFromFMCS()
merchants.map(merchant => MerchantViewModel.getLightWeightInstance(merchant))
}
}
Add a name to the #Cacheable annotation:
#Cacheable(Array("MerchantDataGateway.getAllMerchants"))
It needed a name, or an entry for the value
#Cacheable(value = Array("MerchantDataGateway.getAllMerchants")