Combining test class and configuration class in one single class? - java

I have tried to use the same class both for tests definition and for spring context configuration.
Below is code for class CombineTestAndConfigTry which serves both as test definition and context definition for itself.
bean1 is just stub bean. bean2 should contain the name of the class and bean3 should contain a reference to a class.
It is evident, that Spring is wrapping test class instance into different class, so tests are failed.
Simultaneously, it appeared, that some information can be passed from test class to spring context.
The question is: how normal is such usage and which problems can I meet if utilize it?
Also it is interesting (and how to know), how many times #Autowired is executed? Two times because of both roles? Or one time because Spring sees the situation?
#Configuration
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = CombineTestAndConfigTry.class)
public class CombineTestAndConfigTry {
public static class MyBean1 {
{
System.out.println("MyBean1 constructor");
}
}
public static class MyBean2 {
private String configName;
{
System.out.println("MyBean2 constructor");
}
public String getConfigName() {
return configName;
}
public void setConfigName(String configName) {
this.configName = configName;
System.out.println("MyBean2#configName set");
}
}
public static class MyBean3 {
private CombineTestAndConfigTry testInstance;
{
System.out.println("MyBean3 constructor");
}
public CombineTestAndConfigTry getTestInstance() {
return testInstance;
}
public void setTestInstance(CombineTestAndConfigTry testInstance) {
this.testInstance = testInstance;
System.out.println("MyBean3#testInstance set");
}
}
public String getConfigName() {
return getClass().getSimpleName();
}
#Bean
public MyBean1 myBean1() {
return new MyBean1();
}
#Bean
public MyBean2 myBean2() {
MyBean2 ans = new MyBean2();
ans.setConfigName( getConfigName() );
return ans;
}
#Bean
public MyBean3 myBean3() {
MyBean3 ans = new MyBean3();
ans.setTestInstance(this);
return ans;
}
#Autowired
public MyBean1 myBean1;
#Autowired
public MyBean2 myBean2;
#Autowired
public MyBean3 myBean3;
#Test
public void testGetConfigName() {
assertEquals( getConfigName(), myBean2.getConfigName() );
}
#Test
public void testGetTestInstance() {
assertSame(this, myBean3.getTestInstance());
}
}

My answer to your question is: don't do it (this way).
One of the most important rules of "clean coding" (see the book by Robert Martin) is the SRP - single responsibility principle. Any class (and any method within) is there to do one thing; and one thing only. Meaning: "need of change" should always be coming from one "source"; and not several.
Long story short: what you are asking for is considered (very) bad practice.
If you are doing it in order to avoid another principle DRY (dont-repeat-yourself) ... then think about ways to change your design.

Related

Dynamically load Spring Beans

I am attempting to dynamically load some functionality based off of the environment my application is running in and was wondering if there is a pattern in spring to support this.
Currently my code looks something like this:
public interface DoThingInterface {
void doThing() {}
}
#Conditional(DoThingCondition.class)
#Component
public class DoThingService implements DoThingInterface {
#Override
public doThing() {
// business logic
}
}
#Conditional(DoNotDoThingCondition.class)
#Component
public class NoopService implement DoThingInterface {
#Override
public doThing() {
// noop
}
}
public AppController {
#Autowire
private DoThingInterface doThingService;
public businessLogicMethod() {
doThingService.doThing();
}
}
I appoligise for typing doThing so many times.
But as it currently stands with this, Spring cannot differentiate between the the NoopService and the DothingService since I am autowiring in an interface that both use. The conditionals that they use are directly opposed so there will only ever be one, but Spring does not know this. I had considered using #Profile() instead of conditional, but both will be used in a lot of environment. Is there a correct way to do this so that spring will load only one of these depending on the environment it is in?
Edit: For clarification this functionality is only available in certain deployment regions which is why I chose to use the conditional annotation as the conditions check profile, region, and properties.
As requested, the Conditions are as follows:
public class DoNotDoTheThingCondition implements Condition {
#Override
public boolean matches(ConditionalContext context) {
return !(region.equals(region) && profile.contains("prod"))
}
}
public class DoThingCondition implements Condition {
#Override
public boolean matches(ConditionalContext context) {
return (region.equals(region) && profile.contains("prod"))
}
}
I have simplified the conditions a bit, but that is the general idea. With the code in the state outlined here, Spring throws the following error: no qualifying bean of type DoThingInterface available: expected single matching bean, but found two: DoThingService, NoopService
The solution I came to was to use the condition and manually create the beans as per the comment by Thomas Kasene. I am still unsure why the original did not work, but the key bit was moving the #Conditional annotations onto the beans inside the config. My biggest problem with this method is that you have to maintain parody between the two conditions. That aside it makes for incredibly easy testing as you do not have to stub the noop service if you add your testing profile to the conditions.
The solution ended up looking like this:
Conditions
public class DoNotDoTheThingCondition implements Condition {
#Override
public boolean matches(ConditionalContext context) {
return !(region.equals(region) && profile.contains("prod"))
}
}
public class DoThingCondition implements Condition {
#Override
public boolean matches(ConditionalContext context) {
return (region.equals(region) && profile.contains("prod"))
}
}
Config
#Configuration
public class DoThingConfiguration {
#Conditional(DoThingCondition.class)
#Bean
public DoThingService doThingService() { return new DoThingService(); }
#Conditional(DoNotDoThingCondition.class)
#Bean
public NoopService noopService() { return new NoopService(); }
}
Services
public interface DoThingInterface {
void doThing() {};
}
public class DoThingService {
public void doThing() { // business logic }
}
public class NoopService {
public void doThing() { //Noop }
}
Controller
public class AppController {
private DoThingInterface doThingService;
public businessLogicMethod() {
doThingService.doThing();
}
}

How to automatically inject implementation based on Generics

I have a Service class defined like this,
#RequiredArgsConstructor
class SomeService<T extends AbstractResponse> {
private final ValidationService<T> validationService;
....
}
And I have two kinds of AbstractResponse, ResponseA and ResponseB and have a validation service defined for both of them.
#Service("aValidationService");
class AValidationService<ResponseA> implements ValidationService<ResponseA> {
....
}
and
#Service("ValidationService");
class BValidationService<ResponseB> implements ValidationService<ResponseB> {
....
}
Right now spring is throwing an error because it's not able to deduce the implementation of ValidationService to use in SomeService as there are two implementations of it. How do I make spring deduce the correct implementation based on the type of AbstractResponse?
Hope that I understood your requirements.
You can not automatically inject, when you have (2) of the same kind. In this case ValidationService.
You could inject #ValidationServiceA, or #ValidationServiceB, or a List<ValidationServiceI> and then return the one you want based on a <T> type you care about:
The solution below highlights that.
The method getGenericParameter() is used to return the <T> parameter. This is to avoid the use of Reflection.
The method methodWhichDeterminesWhichServiceToUseBasedOnResponseType to used to determine which ValidationService to use based on the input that you require.
You can find the complete solution below, including a verification Test.
import org.springframework.stereotype.Service;
#Service
public class ValidationServiceA implements ValidationServiceI<ResponseA>{
#Override public Class<ResponseA> getGenericParameter() {
return ResponseA.class;
}
public void print(){
System.out.println("Service A");
}
}
#Service
public class ValidationServiceB implements ValidationServiceI<ResponseB>{
#Override public Class<ResponseB> getGenericParameter() {
return ResponseB.class;
}
public void print(){
System.out.println("Service B");
}
}
public interface ValidationServiceI<T>{
Class<T> getGenericParameter();
void print();
}
#Service
public class ServiceWhichCallsOthers {
#Autowired
private List<ValidationServiceI> validationServices;
public <T> ValidationServiceI<T> methodWhichDeterminesWhichServiceToUseBasedOnResponseType(T responseType){
Optional<ValidationServiceI> validationServiceSupportingResponse = validationServices.stream().filter(validationServiceI -> validationServiceI.getGenericParameter().equals(responseType)).findFirst();
return validationServiceSupportingResponse.get();
}
public void callValidationServiceA(){
methodWhichDeterminesWhichServiceToUseBasedOnResponseType(ResponseA.class).print();
}
public void callValidationServiceB(){
methodWhichDeterminesWhichServiceToUseBasedOnResponseType(ResponseB.class).print();
}
}
#SpringBootTest
public class ServiceWhichCallsOthersIT {
#Autowired
private ServiceWhichCallsOthers serviceWhichCallsOthers;
#Test
public void validateBasedOnResponseType(){
Assertions.assertEquals(ValidationServiceA.class, serviceWhichCallsOthers.methodWhichDeterminesWhichServiceToUseBasedOnResponseType(ResponseA.class).getClass());
Assertions.assertEquals(ValidationServiceB.class, serviceWhichCallsOthers.methodWhichDeterminesWhichServiceToUseBasedOnResponseType(ResponseB.class).getClass());
serviceWhichCallsOthers.callValidationServiceA();
serviceWhichCallsOthers.callValidationServiceB();
}
}

Spring bean scope for "one object per test method"

I have a test utility for with I need to have a fresh instance per test method (to prevent that state leaks between tests). So far, I was using the scope "prototype", but now I want to be able to wire the utility into another test utility, and the wired instances shall be the same per test.
This appears to be a standard problem, so I was wondering if there is a "test method" scope or something similar?
This is the structure of the test class and test utilities:
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyTest {
#Autowired
private TestDriver driver;
#Autowired
private TestStateProvider state;
// ... state
// ... methods
}
#Component
#Scope("prototype") // not right because MyTest and TestStateProvider get separate instances
public class TestDriver {
// ...
}
#Component
public class TestStateProvider {
#Autowired
private TestDriver driver;
// ...
}
I'm aware that I could use #Scope("singleton") and #DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD) but this refreshes more than I need – a new TestDriver instance for each test would be enough. Also, this approach is error-prone because all tests using the TestDriver would need to know that they also need the #DirtiesContext annotation. So I'm looking for a better solution.
It is actually pretty easy to implement a testMethod scope:
public class TestMethodScope implements Scope {
public static final String NAME = "testMethod";
private Map<String, Object> scopedObjects = new HashMap<>();
private Map<String, Runnable> destructionCallbacks = new HashMap<>();
#Override
public Object get(String name, ObjectFactory<?> objectFactory) {
if (!scopedObjects.containsKey(name)) {
scopedObjects.put(name, objectFactory.getObject());
}
return scopedObjects.get(name);
}
#Override
public void registerDestructionCallback(String name, Runnable callback) {
destructionCallbacks.put(name, callback);
}
#Override
public Object remove(String name) {
throw new UnsupportedOperationException();
}
#Override
public String getConversationId() {
return null;
}
#Override
public Object resolveContextualObject(String key) {
return null;
}
public static class TestExecutionListener implements org.springframework.test.context.TestExecutionListener {
#Override
public void afterTestMethod(TestContext testContext) throws Exception {
ConfigurableApplicationContext applicationContext = (ConfigurableApplicationContext) testContext
.getApplicationContext();
TestMethodScope scope = (TestMethodScope) applicationContext.getBeanFactory().getRegisteredScope(NAME);
scope.destructionCallbacks.values().forEach(callback -> callback.run());
scope.destructionCallbacks.clear();
scope.scopedObjects.clear();
}
}
#Component
public static class ScopeRegistration implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) throws BeansException {
factory.registerScope(NAME, new TestMethodScope());
}
}
}
Just register the test execution listener, and there will be one instance per test of all #Scope("testMethod") annotated types:
#RunWith(SpringRunner.class)
#SpringBootTest
#TestExecutionListeners(listeners = TestMethodScope.TestExecutionListener.class,
mergeMode = MergeMode.MERGE_WITH_DEFAULTS)
public class MyTest {
#Autowired
// ... types annotated with #Scope("testMethod")
}
I ran into the same problem some time ago and came to this solution:
Use Mocks
I wrote some methods to create specific mockito settings to add behavior to each mock.
So create a TestConfiguration class with following methods and bean definition.
private MockSettings createResetAfterMockSettings() {
return MockReset.withSettings(MockReset.AFTER);
}
private <T> T mockClass(Class<T> classToMock) {
return mock(classToMock, createResetAfterMockSettings());
}
and your bean definition will look like:
#Bean
public TestDriver testDriver() {
return mockClass(TestDriver .class);
}
MockReset.AFTER is used to reset the mock after the test method is run.
And finally add a TestExecutionListeners to your Test class:
#TestExecutionListeners({ResetMocksTestExecutionListener.class})

Improve explicitness of spring library for extendable config-objects

I am currently working on a spring-library that allows user-defined config-classes (has nothing to to with #Configuration) to be adjusted from another part of the application before they are used:
interface ConfigAdjuster<T extends Config<T>> {
void adjust(T t);
}
abstract class Config<T extends Config<T>> {
#Autowired
Optional<ConfigAdjuster<T>> adjuster;
#PostConstruct
private void init() {
//i know this cast is somewhat unsafe, just ignore it for this question
adjuster.ifPresent(a -> a.adjust((T)this));
}
}
This can be used as follows:
class MyConfig extends Config<MyConfig> {
//imagine many fields of more complex types
public String myData;
}
#Configuration
class MyConfigDefaults {
#Profile("dev")
#Bean
public MyConfig devDefaults() {
//imagine setting defaults values here
return new MyConfig();
}
}
Now a consumer of the library that uses MyConfig can do the following somewhere in his application:
#Bean
public ConfigAdjuster<MyConfig> adjustDefaults() {
return cfg -> {
cfg.myData = "something_other_than_default";
}
}
The biggest problem I see with this approach is that the whole "adjust the config"-part is somewhat hidden for the user. You can not easily tell you are able to change the default-configuration by using a ConfigAdjuster. In the worst case the user tries to autowire the config object and tries to modify it that way which results in undefined behaviour because other components could already have been initialized with the defaults.
Is there an easy way to make this approach more "telling" than what it is right now? The whole idea is to not copy&paste the whole default-config + adjustment parts across multiple projects.
One way to make all of this more explicit would be to require the adjuster in the constructor of Config, but this pollutes every constructor and usage of the inherting classes.
Any thoughts on this?
Edit: Do note that this is a simplified version of the library and I do know about the implications of a private #PostConstruct etc. If you have another way of achieving all of this without the #PostConstruct please do share :)
Edit2:
Let me outline the main goals of this library again:
Allow the definition of default config-objects for the library-user
Allow the enduser (consuming a depedency using this library) to overwrite certain parts of the default configuration before it is used
Save the library-user from boilerplate (e.g. define 2. on their own)
There is two solution for your problem:
1- define a generic Customizer something like:
public interface Customizer<T> {
T customize(T t);
boolean supports(Class target);
}
in your lib you have a config:
public class MyConfig {
private String property;
public MyConfig() {
}
public void setProperty(String property) {
this.property = property;
}
}
so your Default configuration should look something like this:
#Configuration
public class DefaultConfiguration {
#Autowired(required = false)
private List<Customizer> customizers;
#Bean
public MyConfig myConfig() {
MyConfig myConfig = new MyConfig();
myConfig.setProperty("default value");
if (customizers != null) {
for (Customizer c : customizers) {
if (c.supports(MyConfig.class)) {
return (MyConfig) c.customize(myConfig);
}
}
}
return myConfig;
}
}
this way, the only thing the user should do whenever he wants to customize you bean is to implement Customizer, and then declare it as a bean.
public class MyConfigCustomizer implements Customizer<MyConfig> {
#Override
public MyConfig customize(MyConfig myConfig) {
//customization here
return myConfig;
}
#Override
public boolean supports(Class<?> target) {
return MyConfig.class.isAssignableFrom(target);
}
}
and he should declare it:
#Bean
public Customizer<MyConfig> customizer(){
return new MyConfigCustomizer ();
}
I think this answers your question, but it's ugly (uncheched warnings and a List ...) not the best, as everything seems to the user customizable even it's not.
2- I suggest you expose interfaces for Beans that can be adjusted by the user, something like:
public interface MyConfigCustomizer{
MyConfig customize(MyConfig config);
}
your Default Configuration:
#Configuration
public class DefaultConfiguration {
#Autowired(required = false)
private MyConfigCustomizer customizer;
#Bean
public MyConfig myConfig() {
MyConfig myConfig = new MyConfig();
myConfig.setProperty("default value");
if (customizer != null) {
return customizer.customize(myconfig);
}
return myConfig;
}
}
this way the user knows that MyConfig can be adjusted (and not all the beans).

Testing method injected class against existing injector

I have a set of classes that are injected at runtime, because of a legacy code base. I want to write a unit test that checks the injector can satisfy all injected dependencies of those classes. I already have the list of classes to be injected available, and I can instantiate the injector, in Stage.TOOL, because otherwise it will do stuff like connect to a database.
My question is, how do I check those classes against the injector? I've tried injector.getMembersInjector(classToBeInjected), but this fails because injectors in Stage.TOOL do not support it. Basically, I don't need the injector instance, but I need it to check if it could be created.
For reference, here is my current implementation:
#AllArgsConstructor
#RunWith(Parameterized.class)
public class HtmlActionInjectTest {
#Parameters(name="{1}")
public static List<Object[]> parameters() {
return ImmutableList.of(classesUnderTest);
}
#BeforeClass
public static void setUp() {
injector = Guice.createInjector(Stage.TOOL, myLongListOfModules);
}
private static Injector injector;
private final Class<?> actionClass;
#Test
public void test() {
injector.getMembersInjector(actionClass);
}
}
I've also tried using the SPI api, using Elements.getElements(myModules), but I couldn't get it to tell me if the dependencies of the classToBeInjected can be satisfied.
Example of a class under test:
public class MyAction implements SomeInterface {
public MyAction(UnInjectableDependency dep) {
// can't be injected here for legacy reasons
}
#Inject void doInject(SomeDep dep) {
this.dep = dep;
}
#Override void someInterfaceMethod() { /* you get the idea */ }
}
I've found the solution with the help of a colleague.
Guice offers a class called InjectionPoint, which can be used to find all dependencies of the class to be injected. So, the solution looks like this:
#AllArgsConstructor
#RunWith(Parameterized.class)
public class HtmlActionInjectTest {
#Parameters(name="{1}")
public static List<Object[]> parameters() {
return ImmutableList.of(classesUnderTest);
}
#BeforeClass
public static void setUp() {
bindings = Guice.createInjector(Stage.TOOL, myLongListOfModules).getAllBindings().keySet();
}
private static Set<Key<?>> bindings;
private final Class<?> actionClass;
#Test
public void test() {
for (InjectionPoint point : InjectionPoint.forInstanceMethodsAndFields(actionClass)) {
for (Dependency<?> dependency : point.getDependencies()) {
assertTrue("injector cannot satisfy dependency " + dependency.getKey() + " in " + actionClass.getName(), bindings.contains(dependency.getKey()));
}
}
}
}

Categories

Resources