this is my code.
class ACondition extends SpringBootConditoin {
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
if (Config.isA()) {
return new ConditionOutcome(true, "ok");
} else {
return new ConditionOutcome(false, "error");
}
}
}
class BCondition extends SpringBootConditoin {
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
if (Config.isA()) {
return new ConditionOutcome(false, "error");
} else {
return new ConditionOutcome(true, "ok");
}
}
}
#Service
#Conditional(ACondition.class)
class APolicy implements Policy {
...
}
#Service
#Conditional(BCondition.class)
class BPolicy implements Policy {
...
}
class PolicyManager {
#Autowired
#Getter
List<Policy> policyList;
...
}
the default value of Config.isA() is true.
I want to make Config.isA() to return false. so I use Mockito.mockstatic.
#Autowired
PolicyManager manager;
#Test
public void get_B_policy() {
try(MockedStatic<Config> mocked = Mockito.mockStatic(Config.class) {
mocked.when(() -> Config.isA()).thenReturn(false);
List<Policy> policyList = manager.getPolicyList();
assertEquals(1, policyList.size()); // this is right
assertTrue(policyList.get(0) instanceof BPolicy); // this is not right
}
}
Why can't mock the online method?
by the way. If I test the BCondition class, the Config.isA() can be mocked. I can enter the branch which I want. It does not work only in conditional annotation.
Spring Context is already loaded by the time it is reaching the Test Case. Hence, Manager already has selected APolicy.
If you could move the static mock config before spring context loads, then it should match your expectations.
mocked.when(() -> Config.isA()).thenReturn(false);
One way of doing it is initialising the static Mock like below -
Junit4
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {PolicyManager.class, APolicy.class, BPolicy.class})
public class ConditionTest
{
#Autowired
PolicyManager manager;
static MockedStatic<Config> mocked = Mockito.mockStatic(Config.class);
#BeforeClass
public static void setup()
{
mocked.when(() -> Config.isA()).thenReturn(false);
}
#AfterClass
public static void clear()
{
mocked.close();
}
#Test
public void get_B_policy() {
List<Policy> policyList = manager.getPolicyList();
assertEquals(1, policyList.size()); // this is right
assertTrue(policyList.get(0) instanceof BPolicy); // should work now
}
}
Junit5
Please use Jupiter's annotations.
#Autowired
PolicyManager manager;
static MockedStatic<Config> mocked = Mockito.mockStatic(Config.class);
#BeforeAll
public static void setup() {
mocked.when(() -> Config.isA()).thenReturn(true);
}
#AfterAll
public static void clear() {
mocked.close();
}
#Test
public void get_B_policy() {
List<Policy> policyList = manager.getPolicyList();
assertEquals(1, policyList.size()); // this is right
assertTrue(policyList.get(0) instanceof BPolicy); // should work now
}
Related
tl;dr;
I am looking for a way to set a "condition" attribute on a Spring Cacheable annotation from another class. Is there such a way?
Using Spring Cache where it should cache ONLY whenever a certain method has been invoked. This method is in ClassA, the method (data) to cache is in ClassB. What I want to do is something like this:
public ClassA implements myInterface {
...
private Boolean inProcess = false;
public void cacheWhenThisMethodCalled() {
try {
inProcess = true;
// do work here, somewhere along the line
// the method in ClassB gets called
} finally {
inProcess = false;
}
}
ClassB:
public ClassB {
...
#Cacheable(cacheNames={"aCache"}, condition="#classA.inProcess")
public ValueClass findValueClass(UUID id)
However, I can't find the right condition for the SPeL to work. I have tried many combinations, none successfully. ClassA is a SpringBean, but the #Bean annotation returns the Interface, not the class. Can this be made to work? Or is there a better way?
Use a ThreadLocal - you would need to do that anyway for thread safety - otherwise a different thread can change the field.
This works fine...
#SpringBootApplication
#EnableCaching
public class So47580936Application {
public static void main(String[] args) {
SpringApplication.run(So47580936Application.class, args);
}
#Bean
public ApplicationRunner runner(Bar bar) {
return args -> {
bar.cacheFromHere();
bar.dontCacheFromHere();
};
}
#Component
public static class Foo {
#Cacheable(cacheNames = "foos", condition = "T(com.example.So47580936Application$Bar).cacheit()")
public String foo() {
System.out.println("here");
return "foo";
}
}
#Component
public static class Bar {
private static final ThreadLocal<Boolean> cacheit = new ThreadLocal<>();
#Autowired
private Foo foo;
public static boolean cacheit() {
return cacheit.get() == null ? false : cacheit.get();
}
public void cacheFromHere() {
try {
this.cacheit.set(true);
System.out.println("Cache:" + this.foo.foo());
System.out.println("Cache:" + this.foo.foo());
}
finally {
this.cacheit.remove();
}
}
public void dontCacheFromHere() {
System.out.println("Don't:" + this.foo.foo());
System.out.println("Don't:" + this.foo.foo());
}
}
}
result:
here
Cache:foo
Cache:foo
here
Don't:foo
here
Don't:foo
EDIT
Or, you can just make the ThreadLocal a #Bean ...
#SpringBootApplication
#EnableCaching
public class So47580936Application {
public static void main(String[] args) {
SpringApplication.run(So47580936Application.class, args);
}
#Bean
public ApplicationRunner runner(Bar bar) {
return args -> {
bar.cacheFromHere();
bar.dontCacheFromHere();
};
}
#Bean
public ThreadLocal<Boolean> cacheit() {
return new ThreadLocal<>();
}
#Component
public static class Foo {
#Cacheable(cacheNames = "foos", condition = "#cacheit.get() ?: false")
public String foo() {
System.out.println("here");
return "foo";
}
}
#Component
public static class Bar {
#Autowired
private ThreadLocal<Boolean> cacheit;
#Autowired
private Foo foo;
public void cacheFromHere() {
try {
this.cacheit.set(true);
System.out.println("Cache:" + this.foo.foo());
System.out.println("Cache:" + this.foo.foo());
}
finally {
this.cacheit.remove();
}
}
public void dontCacheFromHere() {
System.out.println("Don't:" + this.foo.foo());
System.out.println("Don't:" + this.foo.foo());
}
}
}
I want to write integration tests that will have to use Spring Framework and a custom JPA provider. Straightforward answer as I thought would be to create a test class and annotate it as follows:
#RunWith(SpringRunner.class)
#SpringBootTest
public class Test { ...
Hoping for the all of the default auto-configuration required to happen on its own. But it doesn't the error is:
java.lang.IllegalStateException: Unable to find a #SpringBootConfiguration, you need to use #ContextConfiguration or #SpringBootTest(classes=...) with your test
Can I avoid creating the:
#SpringBootApplication
public class TestApp { ...
and only use src/test/java folder and provide configuration with "#SpringBootTest(classes=...)"? What kind of configuration class do I need then?
I just got the problem let me go back to begining;
First add a configuration class ApplicationContainer
public final class ApplicationContainer {
private static volatile ApplicationContainer singleton;
private ApplicationContext context;
private ApplicationContainer()
{
}
public static ApplicationContainer getInstance()
{
if(ApplicationContainer.singleton == null)
{
synchronized(ApplicationContainer.class)
{
if(ApplicationContainer.singleton == null)
{
ApplicationContainer.singleton = new ApplicationContainer();
}
}
}
return ApplicationContainer.singleton;
}
public void setApplicationContext(ApplicationContext context)
{
this.context = context;
}
public <T> T getBean(Class<T> requiredType)
{
if(this.context == null)
{
throw new IllegalStateException("ApplicationContainer is not started");
}
return this.context.getBean(requiredType);
}
public void start()
{
if(this.context == null)
{
this.context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
}
}
public void stop()
{
if(this.context == null)
{
return;
}
if(this.context instanceof AnnotationConfigApplicationContext)
{
((AnnotationConfigApplicationContext)this.context).close();
}
this.context = null;
}
#Configuration
#ComponentScan("com.domain.package")
public static class ApplicationConfig
{
}}
And change your test class's like that:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {ApplicationContainer.ApplicationConfig.class})
public class YourTestIT {
#Autowired
private ApplicationContext applicationContext;
#Before
public void up() {
ApplicationContainer.getInstance().setApplicationContext(this.applicationContext);
}
#After
public void down() {
ApplicationContainer.getInstance().stop();
}
//test cases
}
then you can #Autowired your repository classes and use directly also you should add IT extension your test class name.
I have created a bean with method that I want to test. Unfortunately it's a bean with a PostConstruct annotation in it. I don't want to call the PostConstruct method.
How can I do this?
I've tried 2 different ways (as shown in the example below) but none working; init() still gets called.
Can someone please give me a detailed example of how to do this?
DirBean.java
#Singleton
#Startup
public class DirBean implements TimedObject {
#Resource
protected TimerService timer;
#PostConstruct
public void init() {
// some code I don't want to run
}
public void methodIwantToTest() {
// test this code
}
}
MyBeanTest.java
public class MyBeanTest {
#Tested
DirBean tested;
#Before
public void recordExpectationsForPostConstruct() {
new Expectations(tested) {
{
invoke(tested, "init");
}
};
}
#Test
public void testMyDirBeanCall() {
new MockUp<DirBean>() {
#Mock
void init() {
}
};
tested.methodIwantToTest();
}
}
MyBeanTest2.java (WORKS)
public class MyBeanTest2 {
#Tested
DirBean tested;
#Before
public void recordExpectationsForPostConstruct() {
new MockUp<DirBean>() {
#Mock
void init() {}
};
}
#Test
public void testMyDirBeanCall() {
tested.methodIwantToTest();
}
}
MyBeanTest3.java (WORKS)
public class MyBeanTest3 {
DirBean dirBean = null;
#Mock
SubBean1 mockSubBean1;
#Before
public void setupDependenciesManually() {
dirBean = new DirBean();
dirBean.subBean1 = mockSubBean1;
}
#Test
public void testMyDirBeanCall() {
dirBean.methodIwantToTest();
}
}
MyBeanTest4.java (FAILS with NullPointerException on invoke())
public class MyBeanTest4 {
#Tested
DirBean tested;
#Before
public void recordExpectationsForCallsInsideInit() {
new Expectations(tested) {
{
Deencapsulation.invoke(tested, "methodCalledfromInit", anyInt);
}
};
}
#Test
public void testMyDirBeanCall() {
tested.methodIwantToTest();
}
}
Move definition of MockUp type to #Before method:
public class MyBeanTest {
#Tested
DirBean tested;
#Before
public void recordExpectationsForPostConstruct() {
new MockUp<DirBean>() {
#Mock
void init() {
}
};
}
#Test
public void testMyDirBeanCall() {
tested.methodIwantToTest();
}
}
We are building an application which uses Spring Boot. We write unit tests using TestNG and Mockito. However I find it pretty annoying to write when(...) configuration, I would like to use real components instead. I started to use #Spy components instead of mocks and this works pretty well until I need to put a Spy into a Spy. I'd like to avoid loading a Spring Context if possible, because creation of the context is very slow it looks like overkill for me to load it for at max 5 classes.
Is there any way, how could I use real code instead of Mocks and not loading whole Spring context? Or is my approach wrong at all and I should mock out all other classes then the tested one?
The other way to do this and may take some modifying of code on your end is to do it by constructor injection instead of field injection. Basically taking away any need of the spring context for testing. so the same from the other answer
Class to test
#Service
public class RecordServiceImpl implements RecordService
{
private final RecordRepository recordRepository;
#Autowired
public RecordServiceImpl(RecordRepository recordRepository)
{
this.recordRepository = recordRepository;
}
public Record find(String id)
{
return recordRepository.findOne(id);
}
public List<Record> findAll()
{
return recordRepository.findAll();
}
#Transactional
public Record save(Record record)
{
record.setRecordStatus("F");
return recordRepository.save(record);
}
}
Test Case
//#RunWith(SpringJUnit4ClassRunner.class)
//#ContextConfiguration(classes = {RecordServiceTestConfig.class})
public class RecordServiceTest
{
// #Autowired
private RecordRepository recordRepository = Mockito.mock(RecordRepository.class);
// #Autowired
private RecordService recordService;
#Before
public void setup()
{
Mockito.reset(recordRepository);
recordService = new RecordServiceImpl(recordRepository);
}
#Test
public void testFind()
{
Mockito.when(recordRepository.findOne(Mockito.anyString())).thenReturn(null);
Record record = recordService.find("1");
Assert.assertNull(record);
Mockito.verify(recordRepository, Mockito.times(1)).findOne(Mockito.eq("1"));
}
#Test
public void testSave()
{
Mockito.when(recordRepository.save(Mockito.any(Record.class)))
.thenAnswer(new Answer<Record>()
{
#Override
public Record answer(InvocationOnMock invocation) throws Throwable
{
Record record = (Record) invocation.getArguments()[0];
Assert.assertEquals("F", record.getRecordStatus());
return record;
}
});
Record record = new Record();
record = recordService.save(record);
Assert.assertNotNull(record);
Mockito.verify(recordRepository, Mockito.times(1)).save(Mockito.eq(record));
}
#Test
public void findAll()
{
Mockito.when(recordRepository.findAll()).thenReturn(new ArrayList<Record>());
List<Record> records = recordService.findAll();
Assert.assertNotNull(records);
Assert.assertEquals(0, records.size());
Mockito.verify(recordRepository, Mockito.times(1)).findAll();
}
}
I think your looking for like this with the use of #ContextConfiguration and #Configuration
Class to test
#Service
public class RecordServiceImpl implements RecordService
{
#Autowired
private RecordRepository recordRepository;
public Record find(String id)
{
return recordRepository.findOne(id);
}
public List<Record> findAll()
{
return recordRepository.findAll();
}
#Transactional
public Record save(Record record)
{
record.setRecordStatus("F");
return recordRepository.save(record);
}
}
Test Class
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {RecordServiceTestConfig.class})
public class RecordServiceTest
{
#Autowired
private RecordRepository recordRepository;
#Autowired
private RecordService recordService;
#Before
public void setup()
{
Mockito.reset(recordRepository);
}
#Test
public void testFind()
{
Mockito.when(recordRepository.findOne(Mockito.anyString())).thenReturn(null);
Record record = recordService.find("1");
Assert.assertNull(record);
Mockito.verify(recordRepository, Mockito.times(1)).findOne(Mockito.eq("1"));
}
#Test
public void testSave()
{
Mockito.when(recordRepository.save(Mockito.any(Record.class)))
.thenAnswer(new Answer<Record>()
{
#Override
public Record answer(InvocationOnMock invocation) throws Throwable
{
Record record = (Record) invocation.getArguments()[0];
Assert.assertEquals("F", record.getRecordStatus());
return record;
}
});
Record record = new Record();
record = recordService.save(record);
Assert.assertNotNull(record);
Mockito.verify(recordRepository, Mockito.times(1)).save(Mockito.eq(record));
}
#Test
public void findAll()
{
Mockito.when(recordRepository.findAll()).thenReturn(new ArrayList<Record>());
List<Record> records = recordService.findAll();
Assert.assertNotNull(records);
Assert.assertEquals(0, records.size());
Mockito.verify(recordRepository, Mockito.times(1)).findAll();
}
}
Test Class Configuration
#Configuration
public class RecordServiceTestConfig
{
#Bean
public RecordService recordService()
{
return new RecordServiceImpl();
}
#Bean
public RecordRepository recordRepository()
{
return Mockito.mock(RecordRepository.class);
}
}
the entire test class took 714ms to run the findAll test took 1ms.
If you are looking to configure your testcase using testng with Spring then you to mention
#ContextConfiguration(locations={
"/context.xml","/test-context.xml"})
at class level to load you spring file and extends your class org.springframework.test.context.testng.AbstractTestNGSpringContextTests
Sample
https://dzone.com/articles/spring-testing-support-testng
The example I tried to follow:
#PrepareForTest(X.class)
public class XTest extends PowerMockTestCase {
#Test
public void test() {
whenNew(MyClass.class).withNoArguments().thenThrow(new IOException("error message"));
X x = new X();
x.y(); // y is the method doing "new MyClass()"
..
}
}
The factory class I am trying to unit test:
public final class LoadableBeanFactory implements ILoadableBeanFactory {
private static final class Loader {
private static final LoadableBeanFactory INSTANCE = new LoadableBeanFactory();
}
private LoadableBeanFactory() { }
public static #Nonnull LoadableBeanFactory getInstance() {
return Loader.INSTANCE;
}
public final #Nonnull <BeanT extends ILoadableBean> BeanT create(final Class<BeanT> beanClass) {
final BeanT optionBean;
try {
final Constructor<BeanT> ctor = beanClass.getConstructor();
optionBean = ctor.newInstance();
return beanClass.cast(optionBean);
} catch(Exception e) {
throw new IllegalArgumentException("Could not instantiate an instance of " + beanClass);
}
}
}
My test is below. The factory does not return the mock. I am thinking that this is because the factory is a singleton that is instantiated and loaded with a private static loader class. So, is there a way to mock this object creation scenario or should I just give up on making this into a true unit test?
#PrepareForTest(LoadableBeanFactory.class)
#Test(groups = {"FactoryTestGroup", "LoadableBeanFactoryTestGroup"})
public class LoadableBeanFactoryTest extends PowerMockTestCase {
#Mock LoadableBean mockBean;
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void shouldCreateBean() {
try {
PowerMockito.whenNew(LoadableBean.class).withNoArguments().thenReturn(mockBean);
LoadableBeanFactory.getInstance().create(LoadableBean.class);
assertEquals(LoadableBeanFactory.getInstance().create(LoadableBean.class), mockBean,
"LoadableBeanFactory should have return mocked bean, but did not: " + mockBean);
} catch(Exception e) {
fail("Failed to mock bean creation");
}
}
}
Why would you even want to do that?
If you wrap the factory in an abstraction (a separate class) then you can inject it via constructor and mock its create method.
public class BeanFactory {
public <BeanT extends ILoadableBean> BeanT create(final Class<BeanT> beanClass) {
return LoadableBeanFactory.getInstance().create(beanClass);
}
}
and now your class that you want to work with
public class SomeClass {
private final BeanFactory beanFactory;
public SomeClass(BeanFactory beanFactory) {
this.beanFactory= beanFactory;
}
public void doSth() {
beanFactory.create(...);
}
}
And then you don't need to have PowerMock at all and your design is really nice.