Unit testing mapstruct with 'uses' - spring-boot

Is there a standard approach for unit testing mappers when using spring boot? i.e. getting dependencies etc.
I'm currently doing something like this:
#SpringBootTest(classes = {VehicleMapper.class, VehicleMapperImpl.class,
VehicleAttributesMapper.class, VehicleAttributesMapperImpl.class})
#RunWith(SpringRunner.class)
public class VehicleMapperTest {
#Autowired
private VehicleMapper vehicleMapper;
VehicleMapper has a uses:
#Mapper(componentModel = "spring", uses = VehicleAttributesMapper.class)
However when I run my unit test the VehicleAttributesMapper does not appear to get invoked? (the types match i.e. source/target)
TIA

Well it turned out that I needed to add an explicit mapping in the VehicleMapper:
#Mapping(source = "vehicleInfo", target = "vehicleAttributes")
I had thought that this would not need to be done explicitly. In the docs under "Invoking other mappers" it seems to suggest you don't (in the example anyway).

Related

Is it posible to make spock specification conditional on property from Spring's application.properties?

Background:
project logic in Java 11 and Spring Boot 2.6.6
some project features are conditionally available depending on specific application properties, some Spring components related with conditional features are also dependent using #ConditionalOnProperty annotation on component
tests (also integration) are written in groovy and spock framework (ver. 2.1-groovy-3.0)
Question:
Is it posible to make spock specification conditional on property from spring's application.properties?
Spock framework provides annotations which make test conditional.
Most accurate seems to be #Requires for my case.
(https://spockframework.org/spock/docs/2.1/all_in_one.html#_requires)
Condition is based on PreconditionContext (https://spockframework.org/spock/docs/2.1/all_in_one.html#precondition_context).
Simplified Specificatiotion example (two working #Requires annotations left as example, but they do not check what is needed in my case):
import org.spockframework.runtime.extension.builtin.PreconditionContext
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.ActiveProfiles
import org.springframework.test.context.ContextConfiguration
import spock.lang.Requires
import spock.lang.Specification
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ActiveProfiles('integration')
#ContextConfiguration(classes = TestSpringBootApplication)
//TODO: How to make this feature dependent of property from application.properties?
//#Requires(reason = 'Specification for AAA feature enabled', value = { isFeatureAAAEnabled() })
//#Requires(reason = 'Test run only on Linux', value = { PreconditionContext preconditionContext -> preconditionContext.os.windows })
class ConditionalSpec extends Specification {
//Some conditional components #Autowired
//feature methods
def "one plus one should equal two"() {
expect:
1 + 1 == 2
}
private static boolean isFeatureAAAEnabled() {
true
}
}
What do you want exactly, is it enough to just not run any tests but still start the spring context, or do you want to also avoid starting the spring context?
If it is the first one, then you can use instance or shared from the Precondition Context. If you enable shared field injection you should be able to do this.
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ActiveProfiles('integration')
#ContextConfiguration(classes = TestSpringBootApplication)
#EnableSharedInjection
#Requires(reason = 'Specification for AAA feature enabled', value = { shared.myValue == 'featureAAA' })
class ConditionalSpec extends Specification {
#Value('${value.from.file}')
#Shared
String myValue
//feature methods
def "one plus one should equal two"() {
expect:
1 + 1 == 2
}
}
If you can't use shared injection due to it's limitations, then you'll have to replace shared by instance in the condition.
If you want to avoid starting spring, then you'll have to write your own extension to figure out what the value from the application.properties, and skip the spec yourself.

Is it possible to only load specific Annotations based on a profile?

Is it possible to only load specific Annotations only during tests or only during a run in Spring Boot?
I am facing a situation where there are Annotations affecting the tests, yet work well in the live run, so wanted to know whether it was possible to exclude them only during tests, but include them when running, similar to how one can include specific beans based on a Spring profile
Apologies if this has been asked before, I have tried searching to no avail
You could use the #ConditionalOnProperty annotation which creates a bean depending on which property (in the application.properties -> app.val = false) is set. For example for a service:
#Service
#ConditionalOnProperty(name = "app.val", havingValue = "false")
public class TestService {
...
}
Also you could use the #Profile annotation and annotate them to the methods which have for example a test profile (defined in the application.properties as well -> spring.profiles = test).
#Profile({"test"})
public String getValue() {
return "test value";
}
#Profile({"production"})
public String getValue() {
return "production value";
}

How to resolve a lazy association on a integration test not using the #Transactional?

I'm doing some integration tests with Spring but I'm trying to simplify my life when asserting the content of some entities that I need to resolve a lazy before.
My test is like:
#DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class OrderIT {
#Autowired
private OrderService orderService;
#Autowired
private OrderRepository orderRepository;
#Test
public void test01CreateOrder() {
OrderDto orderDto = createSomeOrderDto();
orderService.create(orderDto);
Order order = orderRepository.findByNumber("123456");
// order asserts ...
}
}
After that we have tests on the same class that use this order (tests like changing the order, etc). This is why I'm not using #Transactional on the test method and the integration tests are executed respecting the order name, because I use the previous test as base for the next test.
This works very good. But my problem is when I need to improve my asserts trying to resolve some lazy information of the entity.
I can't just, per example, do a order.getReponsable() on my test because I will receive a LazyException error.
What works (but I don't like it)
The only way I found to do that on tests is creating a specific query on the repository fetching this responsable. I can call this query and the lazy association will no be a problem anymore. But I would not like to create repository methods just for testing purposes.
What I tried (and failed)
My idea was inject the TestEntityManager on my integration test with #AutoConfigureTestEntityManager on the test class, on the hope to access the getEntityManager and, finally, create a query on the test, like this:
Order order = (Order) testEntityManager.getEntityManager().createQuery("SELECT o FROM Order o JOIN FETCH o.responsable WHERE o.number = " + "123456").getSingleResult();
Will be perfect if worked. But the EntityManager is not available:
> java.lang.IllegalStateException: No transactional EntityManager found
There is another alternative? Again, the #Transactional on the test method is not an option for me.
I just found an alternative. I don't know if spring offer some better, but worked.
I just inject the EntityManagerFactory on my integration test class:
#Autowired
private EntityManagerFactory entityManagerFactory;
And made my query using the entity manager, but I created one:
Order order = (Order) entityManagerFactory.createEntityManager().createQuery("SELECT o FROM Order o JOIN FETCH o.responsable WHERE o.number = " + "123456").getSingleResult();

how to pre-populate spring properties from tests

I've a slight race condition when it comes to loading spring properties for an integration test using #TestPropertySource.
Consider the following;
test (using Spock but same for JUnit)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#TestPropertySource(locations = "classpath:test/simple-test.properties")
class SimpleStuff extends Specification {
public static final String inputDirectoryLocation = "/tmp/input-test-folder"
def "test method"() {
//do test stuff
}
}
simple-test.properties
inputDirectoryLocation=/tmp/input-test-folder
Spring Component
#Component
class SpringComponent {
#Value('${inputDirectoryLocation}')
String inputDirectory;
//do other stuff
}
The above works fine but how would I make the test fully isolated and NOT have a dependency on the FileSystem having the folder /tmp/input-test-folder (as not all users running this test are allowed to create a /tmp folder on their FS)
For example, I would like to use something like
inputDirectoryLocation = Files.createTempDirectory()
so that
#Value('${inputDirectoryLocation}')
String inputDirectory;//equals the output of Files.createTempDirectory()
resulting in test using the OS default temporary folder location & allows us to have the test simply delete the temp folder on cleanup. Is there an eloquent solution to solve the above?
Note: using Spring boot 1.5
Turned out simple enough - simply had to change the value in the properties file to refer to the
inputDirectoryLocation=${java.io.tmpdir}/input-test-folder
Then have my Spock specification create the temp folder prior to launching Spring (by using the setup() fixture method )

Spring profiles on integration tests class

we have selenium tests which are ran by java test class.
On local environment everything is ok, but I want to switch off those tests when run on jenkins.
So I use:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebIntegrationTest("server.port=1234")
#Profile("!jenkins")
#ActiveProfiles("integrationtests")
public class LoginAndEditProfileSeleniumTest {
...
What works:
running mvn clean test run all tests locally, with integrationtests profile active. I dont want to pass any additional parameter.
What I want to achieve:
running mvn clean test -Dspring.profiles.active=jenkins switch off this test.
Can I merge somehow profile passed by parameter, ActiveProfile annotation and take Profile annotation into consideration? :)
//update:
Its possible to use class extending ActiveProfilesResolver:
public class ActiveProfileResolver implements ActiveProfilesResolver {
#Override
public String[] resolve(Class<?> testClass) {
final String profileFromConsole = System.getProperty("spring.profiles.active");
List<String> activeProfiles = new ArrayList<>();
activeProfiles.add("integrationtests");
if("jenkins".contains(profileFromConsole)){
activeProfiles.add("jenkins");
}
return activeProfiles.toArray(new String[activeProfiles.size()]);
}
}
but it seems to not to cooperate with #Profile anyway ( jenkins profile is active but test is still running ) .
#Profile has zero affect on test classes. Thus, you should simply remove that annotation.
If you want to enable a test class only if a given system property is present with a specific value, you could use #IfProfileValue.
However, in your scenario, you want to disable a test class if a given system property is present with a specific value (i.e., if spring.profiles.active contains jenkins).
Instead of implementing a custom ActiveProfileResolver, a more elegant solution would be to use a JUnit assumption to cause the entire test class to be ignored if the assumption fails.
This should work nicely for you:
import static org.junit.Assume.*;
// ...
#BeforeClass
public static void disableTestsOnCiServer() {
String profilesFromConsole = System.getProperty("spring.profiles.active", "");
assumeFalse(profilesFromConsole.contains("jenkins"));
}
Regards,
Sam (author of the Spring TestContext Framework)

Resources