Create custom testcontainer modules? - java

I am using JUnit5 + Testcontainers.
I see there are Testcontainer modules such as these: https://github.com/testcontainers/testcontainers-java/tree/main/modules
I would like to build my own Testcontainers custom module.
Is there a way for developers to build their own?

Yes, you can extend the GenericContainer class. By overriding methods like containerIsStarted or adding your own methods, you can customize the container's behavior.
Have a look at how these modules are implemented, e.g., the MongoDB one. It overrides containerIsStarted to initialize a replica set:
#Override
protected void containerIsStarted(InspectContainerResponse containerInfo, boolean reused) {
if (reused && isReplicationSetAlreadyInitialized()) {
log.debug("Replica set already initialized.");
} else {
initReplicaSet();
}
}

Related

Spring conditionally require bean only if profile is active

I have a class which is disabled based on #Profile. I want to use it inside another class that is not conditional on the same profile:
#Component
#Profile("!local")
public class NotAlwaysExistingClass {
public void doThing() {
...
}
}
public class AlwaysExistingClass {
#Autowired(required=true)
NotAlwaysExistingClass notAlwaysExisting;
// Impossible for this to happen if profile is "local"
public void notAlwaysDoneThing() {
notAlwaysExisting.doThing();
}
...
}
I don't want to set the #Autowired(required=false) in all cases. Is it possible to disable the requirement only if a certain profile is active? I want to do this to make it more convenient to occasionally run the code locally, but without compromising the application or making major changes to the class structure.
I agree with #xerx593's #1 but you could also change that a little. You could extract an interface and make the class depending on it use it via an interface. Then you would have 2 beans that implement that interface and only available at a given time via #Profile selection. Remember #Autowired is by type by default.
Really this issue is similar (or the same) to having a couple of profiles for various needs of a datasource for example. In my projects, the local profile points to a local DB, the regular one points to some cloud db via env variables or whatever, and then I have a "cicd" profile for integration tests and those use a spun up H2 DB.
"Smart" (tricky) (?) approach:
NO-OP Bean/Profile ;)
Introduce an other "bean" (or "class"), which:
extends NotAlwaysExistingClass
takes #Profile("local") (so the logical complement of the "non-local" profile)
overrides doThing(), but with no-op/cheap/only logging code.
Done.
you don't need (further) refactorings
you can leave the required attribute (one of the profiles will always strike)
in "non-local" profile, you get the right bean
in "local" profile: nice logging/no-op :)

Disabling Swagger Bundle in DropWizard

I am looking to disable the swagger functionality / endpoint in a production environment based on a config value.
How would I go about this?
I believe the best way to achieve this is not to add the bundle during the execution of the initialize method when a DropWizard application first starts.
The issue with this solution is that you cannot access the configuration get methods that are populated from the values in the YAML/YML file. These values are available are available when we the application gets to the run method.
Here is my initialise method from the application class
#Override
public void initialize(Bootstrap<Configuration> bootstrap) {
LOGGER.debug("initialize");
bootstrap.setConfigurationSourceProvider(new SubstitutingSourceProvider(bootstrap.getConfigurationSourceProvider(),
new EnvironmentVariableSubstitutor(false)));
bootstrap.addBundle(new SwaggerBundle<Configuration>() {
#Override
protected SwaggerBundleConfiguration getSwaggerBundleConfiguration(Configuration configuration) {
return configuration.swaggerBundleConfiguration;
}
});
}
If I need to clarify more please let me know.
Thanks in advance.
You can set an environment variable in production and use it to decide whether to include SwaggerBundle or not. For example:
if (!"prod".equalsIgnoreCase(System.getenv("ENVIRONMENT"))) {
bootstrap.addBundle(new SwaggerBundle<Configuration>() { ... }
}
I was using an older version of DropWizard at the time.
After updating, there were new methods available including setIsEnabled()
This is what was added to solve the issue.
bootstrap.addBundle(new SwaggerBundle<Configuration>() {
#Override
protected SwaggerBundleConfiguration getSwaggerBundleConfiguration(Configuration configuration) {
if(!configuration.getSwaggerEnabled()){
configuration.swaggerBundleConfiguration.setIsEnabled(false);
}
return configuration.swaggerBundleConfiguration;
}
});
}
Thanks,

Design : How to drive a dynamic set of similar classes from a program

I have to make a design decision in my application. The following is the scenario.
I have a set of service classes that are spread across different maven modules in my application.
public class ServiceA
{
public void startA()
{
....
}
}
public class ServiceB
{
public void startB()
{
....
}
}
I have got 8 such service classes currently.More may be added in future.
All of these service classes' start has to be called in a driver program.
The number of services may vary with time.And when it does,I want to avoid updating Driver program.
I have thought about making the services to implement an interface.But at runtime, the list of services needs to be available for the driver to start them.
Driver is the first to be executed when the application is launched and is expected to start the services.
Please suggest as to how to go about this.
You could turn it around and make all services register themselves to the driver.
They can all be spring beans with the driver injected.
If you don't want the services to know about the driver, you can introduce either a middle man for them that has a dependency on both the service and the driver, or you can to it trough a #Configuration class.
Update:
You can use the ServiceLoader class to locate your services have a look at this tutorial.
And here is a library that makes it somewhat easier to use: ServicePluginLoader
Have them all implement a common interface that includes a start() method.
Load them with Class.forName() after obtaining the fully qualified class names and create new instances via reflection (Class.newInstance()) which will require that they all have no-arg constructor.
Spring already does the class and instance management part, so you should probably investigate it, leaving you with only the common interface to worry about.
Creating a configuration class to detect and register only the classes that implement the IsService interface.
#Configuration
#ComponentScan(basePackages = {"com.subex.roc"} ,
useDefaultFilters = false ,
includeFilters = {#Filter(type = FilterType.ASSIGNABLE_TYPE,value = IsService.class)})
public class ServiceRegisterConfig
{
}
Fetching the registered classes and calling start() polymorphically.
public void initServerServices()
{
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(ServiceRegisterConfig.class);
context.refresh();
for (String beanDfn : context.getBeanDefinitionNames())
{
if (context.getBean(beanDfn) instanceof IsService) {
IsService service = (IsService) context.getBean(beanDfn);
service.start();
}
}
}

Where should I move a library initialization when using Dagger 2?

I have this code that initializes Calligraphy default configuration.
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
// The initialization I want to move
CalligraphyConfig.initDefault(new CalligraphyConfig.Builder()
.setDefaultFontPath("fonts/MyFont.ttf")
.build()
);
}
}
I want to use Dagger 2 in my project but I don't fully understand what classes should I create and where to move this code in order to keep the project clean ?
In short, you probably wouldn't move anything. The problem with this library is that it uses static methods for initialization and utilization. Static methods are a pain when trying to do dependency injection.
The Library (or why you would not change anything)
It looks like this library is 'just' about switching the used fonts by wrapping the context. As such it does not really provide business logic to your project, but just adds to your views / UI.
Injecting a dependency rather than just calling static methods is most useful if you either want to be able to unit test (inject mocks) or easily swap modules / behavior. In the case of globally changing fonts, both seems less likely.
If on the other hand you really need (or want to) be able to test it, or just have a clean design...
...wrap it
Static methods are a pain, because you can not have objects holding the logic. Unless you wrap them. To properly do DI with static methods, you would have to define your own interface.
public interface CalligraphyManager {
/**
* Called on app start up to initialize
*/
void init();
// other methods, like wrapping context for activity
Context wrap(Context context);
}
You now have some manager to access the static methods. The implementation should be fairly simple, since you want to do proper DI the application context and path needed for init() would be passed into the constructor of your implementation. The creation of your manager can thus be handled by your ApplicationModule—just add some provides method
#Singleton
#Provides
// You would also have to provide the path from somewhere or hardcode it
// left as an exercise for the reader
CalligraphyManager provideCalligraphyManager(Context context, String path) {
return new ActualCalligraphyManager(context, path);
}
Your application would then look something like this:
public class MyApplication extends Application {
#Inject
CalligraphyManager mCalligraphy;
#Override
public void onCreate() {
super.onCreate();
mComponent = DaggerAppComponent.builder()
.appModule(new AppModule(this))
.build();
mComponent.inject(this);
// call the initialization
mCalligraphy.init();
}
}
Everything else is as usual. You have a singleton object in your application components graph, you can thus inject the same object into your activities and call `wrap´ where appropriate.
What about testing / mocking?
Since the whole reason of doing this is to make it 'testable', you can now easily provide a mock / stub object.
Create another implementation of the manager where init() would just do nothing, and wrap(Context) would just return the same context—a simple stub object.

Change TestListener for Unitils DbUnitModule

I am attempting to use Unitils to assist me in Database testing. I would like to use the Unitils/DBMaintain functionality for disabling constraints. However there is a few problems with this. I do not wish to use DBMaintain to create my databases for me however I wish to use its constraint disabling functionality. I was able to achieve this through the use of a custom module listed below:
public class DisableConstraintModule implements Module {
private boolean disableConstraints = false;
public void afterInit() {
if (disableConstraints) {
DatabaseUnitils.disableConstraints();
}
}
public void init(Properties configuration) {
disableConstraints = PropertyUtils.getBoolean("Database.disableConstraints", false, configuration);
}
}
This partially solves what I want however I wish to be able to only disable constraints for tables I will be using in my test. My tests will be running against a database with multiple schemas and each schema has hundreds of different tables. DatabaseUnitils.disableConstraints() disables the constraints for every table in every schema which would be far too time consuming and is unnecessary.
Upon searching the dbmaintain code I found that the Db2Database class does indeed contain a function for disabling constraints on a specific schema and table name basis however this method is protected. I could access this be either extending the Db2Database class or using reflection.
Next I need to be able to determine which schemas and tables I am interested in. I could do this by observing the #DataSet annotation to determine which schemas and tables are important based on what is in the xml. In order to do this I need to override the TestListener so I can instruct it to disable the constraints using the xml before it attempts to insert the dataset. This was my attempt at this:
public class DisableConstraintModule extends DbUnitModule {
private boolean disableConstraints = false;
private TableBasedConstraintsDisabler disabler;
public void afterInit() {
}
public void init(Properties configuration) {
disableConstraints = PropertyUtils.getBoolean("Database.disableConstraints", false, configuration);
PropertyUtils.getInstance("org.unitils.dbmaintainer.structure.ConstraintsDisabler.implClassName", configuration);
}
public void disableConstraintsForDataSet(MultiSchemaDataSet dataSet) {
disabler.disableConstraints(dataSet);
}
protected class DbUnitCustomListener extends DbUnitModule.DbUnitListener {
#Override
public void beforeTestSetUp(Object testObject, Method testMethod) {
disableConstraintsForDataSet(getDataSet(testMethod, testObject));
insertDataSet(testMethod, testObject);
}
}
}
This is what I would like to do however I am unable to get the #DataSet annotation to trigger my DbUnitCustomListener and instead it calls the default DBUnitModule DbUnitListener. Is there anyway for me to override which listener gets called when using the #DataSet annotation or is there a better approach all together for disabling constraints on a specific schema and table level for a DB2 Database?
Thanks
You have to tell Unitils to use your subclass of DbUnitModule. You do this using the unitils.module.dbunit.className property in your unitils.properties file. It sounds like you've got this part figured out.
The second part is to override DbUnitModule's getTestListener() in order to return your custom listener.
See this post for an example.

Categories

Resources