Order of multiple extensions in JUnit 5 - java

If I use more than one extension with JUnit 5, whats the order? Ideally the order int the #ExtendsWith annotation is respected, but I could not find any documentation about that.
Example:
#ExtendWith({SpringExtension.class, InitH2.class})
public class VmRepositoryIntegrationTest {
// Test implemenations
}
So in this example I need Spring to set up the DB connection before I cann initialize the DB.

From §5.2.1 of the JUnit 5 User Guide:
...
Multiple extensions can be registered together like this:
#ExtendWith({ DatabaseExtension.class, WebServerExtension.class })
class MyFirstTests {
// ...
}
As an alternative, multiple extensions can be registered separately like this:
#ExtendWith(DatabaseExtension.class)
#ExtendWith(WebServerExtension.class)
class MySecondTests {
// ...
}
Extension Registration Order
Extensions registered declaratively via #ExtendWith will be executed in the order in which they are declared in the source code. For example, the execution of tests in both MyFirstTests and MySecondTests will be extended by the DatabaseExtension and WebServerExtension, in exactly that order.

Related

Use ArchUnit As Adapter to Run Architecture Test Based on External AnalyzeClasses

I am trying to do one example with ArchUnit where passing the AnalyzeClasses can be dynamic based on for which Adapter Application the test need run.
For Example:
#AnalyzeClasses(packages = "${archtest.scan.package}", importOptions = { ImportOption.DoNotIncludeTests.class, ImportOption.DoNotIncludeJars.class })
public class ArchitectureTests {
}
And from application.properties file it should allow to pass the packages to analyze dynamically, so any application using this Application as Jar library can provide the scan classes in its properties file. As below.
archtest.scan.package=com.example.pkgname
I am not sure what is the right way to pick up the dynamic value from property and pass that into #AnalyzeClasses Annotation. I am looking for some help or any example in this regard.
I don't think that ArchUnit's JUnit 4 & 5 support – in the current version 0.23.1 – allows for dynamic packages configured via an application.properties.
But instead of using #AnalyzeClasses, you can always just invoke new ClassFileImporter().import… and pass any dynamic runtime values you like.
(Note that ArchUnit's JUnit support also introduces a clever cache to reuse imported JavaClasses by multiple #ArchTests, but storing JavaClasses in a static field may be also good enough.)
This actually should be possible using a custom LocationProvider within #AnalyzeClasses. E.g.
#AnalyzeClasses(locations = ApplicationPropertiesLocationProvider.class)
public class ExampleTest {
// ...
}
class ApplicationPropertiesLocationProvider implements LocationProvider {
#Override
public Set<Location> get(Class<?> testClass) {
String packageToScan = readFromApplicationProperties();
return Locations.ofPackage(packageToScan);
}
}
But be aware of caching limitations! The caching mechanism assumes that your LocationProvider is "idempotent", i.e. it always returns the same locations. The caching mechanism will only take the type of the LocationProvider into consideration as cache key. This should not be a problem for a static application.properties as source though.

How to pass multiple consumer names in pact provider JUnit test

Using the pact provider JUnit 5/Spring Boot support annotations, perhaps I am not searching well for the answer... I'm wondering if it's possible to annotate a pact provider verification test with multiple consumers using the #Consumer annotation.
Like I would want to be able to do something like the following
#Provider("provider-name")
#Consumer("consumer-1, consumer-2")
#PactBroker
#ActiveProfiles("test")
public class PactVerificationTest {
#Test
//test methods
//...
}
The annotation takes a String as a value so unfortunately something like #Consumer({"consumer-1", "consumer-2"}) does not work either.
Like this:
#PactBroker(consumerVersionSelectors = {
#VersionSelector(consumer = "my-consumer-1"),
#VersionSelector(consumer = "my-consumer-2")
})
Use the latest library version and see documentation for more

Register Extensions automatically in JUnit

I am extending JUnit's ParameterResolver to provide a custom argument in Test methods.
public class MyParameterResolver implements ParameterResolver {
#Override
public boolean supportsParameter(ParameterContext parameterContext,
ExtensionContext extensionContext) throws ParameterResolutionException {
return parameterContext.getParameter().getType() == MyWrapper.class;
}
#Override
public Object resolveParameter(ParameterContext parameterContext,
ExtensionContext extensionContext) throws ParameterResolutionException {
// Do something about getting MyAnnotation
return new MyWrapper();
}
}
and then using it in test methods using ExtendWith
#ExtendWith(MyParameterResolver.class)
#Test
#MyAnnotation(val = 20)
#MyAnnotation(val = 30)
void test(MyWrapper wrapper) {
System.out.println(wrapper);
}
It works perfectly however I do not like the idea that all the tests which needs this extension would need to be annotated with #ExtendWith(MyParameterResolver.class) , is it possible to automatically register the annotation in Junit programmatically whenever a test method contains MyWrapper parameter or MyAnnotation annotation?
§5.2 of the JUnit 5 User Guide shows the three ways one can register extensions:
Declaratively with #ExtendWith (§5.2.1).
Developers can register one or more extensions declaratively by annotating a test interface, test class, test method, or custom composed annotation with #ExtendWith(…​) and supplying class references for the extensions to register.
Note: If you annotate the class with #ExtendWith(...) then all enclosed test methods will be extended by the specified extension.
Programmatically with #RegisterExtension (§5.2.2).
Developers can register extensions programmatically by annotating fields in test classes with #RegisterExtension.
When an extension is registered declaratively via #ExtendWith, it can typically only be configured via annotations. In contrast, when an extension is registered via #RegisterExtension, it can be configured programmatically — for example, in order to pass arguments to the extension’s constructor, a static factory method, or a builder API.
Automatically via java.util.ServiceLoader (§5.2.3).
In addition to declarative extension registration and programmatic extension registration support using annotations, JUnit Jupiter also supports global extension registration via Java’s java.util.ServiceLoader mechanism, allowing third-party extensions to be auto-detected and automatically registered based on what is available in the classpath.
Specifically, a custom extension can be registered by supplying its fully qualified class name in a file named org.junit.jupiter.api.extension.Extension within the /META-INF/services folder in its enclosing JAR file.
Note: Automatic registration must be explicitly enabled.
If you're okay with your extension being registered globally then you can use the automatic registration mechanism. But there does not appear to be a way to only extend test methods if they have a specific parameter type. However, you may be able to create a so-called composed annotation to make things simpler:
#ExtendWith(MyParameterResolver.class)
#Retention(RUNTIME)
#Target(METHOD)
public #interface ComposedAnnotation {
int[] vals(); // int array to (maybe?) replace the multiple #MyAnnotation(val = XXX) annotations
}
Though I don't know if that's compatible with your #MyAnnotation annotation.
Note that if your implementation of #supportsParameter(ParameterContext,ExtensionContext) is quick then having the extension registered for methods which don't use it should not be a problem. In other words, it won't significantly slow down your tests or, as far as I know, cause errors.

Get a different class based on a config value in JAVA

I am working on a program that supports 3 different platforms. These platforms have identical logic, but the program would have to work with a different database for each one.
I have three different Database.java files for each platform. For example
com.myproject.dao.bmw.Database.java
com.myproject.dao.ford.Database.java
com.myproject.dao.chevy.Database.java
The Database classes all have the same method signatures. But their database connection or queries may be different.
I set the platform name, which in this case is the car make using a config.properties file. I call the methods inside the Database class depending on which platform is set in the config.properties file throughout the program many times.
I want to have to get the Database object based in what is set on the config.properties file when the program starts, while having the same object name for the database. That way each time I call the method names I would not have to use if statements or switches each time I want to use a method in the Database class.
What is the best way to achieve my goal?.
This sounds like a job for the Factory pattern.
Create an interface CarDB (or ICarDb if you like the naming convention like that so you know it is an interface) that contains all the common methods
Create 3 classes that implement CarDB - Ford, Bmw and Chevy
Create a CarDbFactory that has a method like CarDB getDb(Params params) that given your parameters will return a CarDB - the actual one (Ford, Bmw...) depends on the paremeters.
First of all, you did not mention any reasons why you are not considering any of the existing ORM frameworks like Hibernate which is meant specifically for this job. In a nutshell, the ORM allows you to switch across the different databases easily. But if you have a strong reason for not to use the ORM framework, then you can consider the below approach.
Basically, need to define and use the DataBaseConfigFactory and set the appropriate DBConfiguration during the start up of your application as shown below:
DataBaseConfigFactory interface:
public interface DataBaseConfigFactory {
Connection getConnection();
void executeQuery();
}
MyProjectDataBaseConfigFactory class:
public class MyProjectDataBaseConfigFactory implements DataBaseConfigFactory {
private static final DBConfiguration dbConfiguration;
static {
// Get the active db name from props file
// Set dbConfiguration to BmwDBConfiguration or FordDBConfiguration, etc...
}
public Connection getConnection() {
return dbConfiguration.getConnection();
}
public void executeQuery() {
return dbConfiguration.executeQuery();
}
}
Now define a DBConfiguration interface and all specific implementations for the operations that your bmw, ford, etc.. support
DBConfiguration class:
public interface DBConfiguration {
//Add all methods that can be supported by DBConfiguration
}
public class BmwDBConfiguration implements DBConfiguration {
// BMW specific implementations for DBConfiguration
}
public class FordDBConfiguration implements DBConfiguration{
// Ford specific implementations for DBConfiguration
}
In short, you will be using DataBaseConfigFactory interface through out your application to connect with databases and if a new database is added then you need to set the DBConfiguration appropriately.

Use Spring Data repositories to fill in test data

I'd like to ask whether it is alright to use apps repositories(Spring Data based) to fill in testing data. I know I can use sql file with data, but sometimes I need something more dynamical. I find writing sql or datasets definitions cumbersome(and hard to maintain in case of schema change). Is there anything wrong with using app repositories? There are all basic CRUD operations already there. Note we are talking especially about integration testing.
I feel it is kind of weird to use part of app to test itself. Maybe I can create another set of repositories to be used in test contexts.
No, there is absolutely nothing wrong with using Spring Data repositories to create test data.
I even prefer that since it often allows for simpler refactoring.
As with any use of JPA in tests you need to keep in mind that JPA implementations are a write-behind cache. You probably want to flush and clear the EntityManager after setting up the test data, so that you don't get anything from the 1st level cache that really should come from the database. Also, this ensures data is actually written to the database and problems with that will surface.
You might be interested in a couple of articles about testing with Hibernate. They don't use Spring Data, but it would work with Spring Data JPA just the same.
I would recommand to use Flyway to setup your databases and use Flyway test extension for integration testing.
So that you can do something like that:
#ContextConfiguration(locations = {"/context/simple_applicationContext.xml"})
#TestExecutionListeners({DependencyInjectionTestExecutionListener.class,
FlywayTestExecutionListener.class})
#Test
#FlywayTest(locationsForMigrate = {"loadmsql"}) // execution once per class
public class MethodTest extends AbstractTestNGSpringContextTests {
#BeforeClass
#FlywayTest(locationsForMigrate = {"loadmsql"}) // execution once per class
public static void beforeClass() {
// maybe some additional things
}
#BeforeMethod
#FlywayTest(locationsForMigrate = {"loadmsql"}) // execution before each test method
public void beforeMethod() {
// maybe before every test method
}
#Test
#FlywayTest(locationsForMigrate = {"loadmsql"}) // as method annotation
public void simpleCountWithoutAny() {
// or just with an annotation above the test method where you need it
}

Categories