Delegate mocks to separate class - java

Instead of having this:
#ExtendWith(SpringExtension.class)
#SpringBootTest(classes = {Application.class}, webEnvironment =
SpringBootTest.WebEnvironment.DEFINED_PORT)
public abstract class AbstractIT {
#MockBean
private FooAdapter fooAdapter;
#MockBean
private BarAdapter barAdapter;
public void mockFoo() {
FooResponse dto = new FooResponse();
when(fooAdapter.fooRequest()).thenReturn(dto);
}
I want to have that:
#ExtendWith(SpringExtension.class)
#SpringBootTest(classes = {Application.class}, webEnvironment =
SpringBootTest.WebEnvironment.DEFINED_PORT)
public abstract class AbstractIT {
#MockBean/Autowired?
MockProvider mockProvider;
class MockProvider {
#MockBean
private FooAdapter fooAdapter;
#MockBean
private BarAdapter barAdapter;
public void mockFoo() {
FooResponse dto = new FooResponse();
when(fooAdapter.fooRequest()).thenReturn(dto);
}
However, I don't know if that is even possible using Mockito with SpringRunner. Since we have a lot of adapters (like 10), I do not want to pollute the AbstractIT too much, hence I'd like to delegate the initialization and concrete mocking of those dependencies out to another class taking care of that.

You have to specify that provider class in the #ContextConfiguration for each test you want to use it for:
#ExtendWith(SpringExtension.class)
#SpringBootTest(classes = {Application.class}, webEnvironment = WebEnvironment.DEFINED_PORT)
#ContextConfiguration(classes = {MockProvider.class} // <-- this
public abstract class AbstractIT {
You have to make sure to make the provider a configuration class:
#Configuration
public class MockProvider {
As per #MockBean documentation:
The annotation can be used directly on test classes, on fields within
your test, or on #Configuration classes and fields.

Related

How to use #SpringBootTest class mockbean in test class?

#SpringBootTest(classes = TestConfig.class)
class ServiceIntegTest {
}
class TestConfig {
#MockBean
RandomExecutor randomExecutor
}
I want to use RandomExecutor mock bean in ServiceIntegTest class, how to do it ?
I am not mocking methods of the bean in TestConfig class itself, because in ServiceIntegTest class there are various tests in which methods of RandomExecutor have to behave in different ways.
You do not have to #MockBean in your config, you have to do it in the test class. Then you can mock it in some test classes and use a real instance in others.
Have a look at a basic usage of #MockBean:
https://www.infoworld.com/article/3543268/junit-5-tutorial-part-2-unit-testing-spring-mvc-with-junit-5.html
You use a MockBean as you would a #Mock It just get injected into a spring context you're using for your test.
#SpringBootTest
class ServiceIntegTest {
#MockBean RandomExecutor randomExecutor;
// this service gets autowired from your actual implementation,
// but injected with the mock bean you declared above
#Autowired
YourService underTest;
#Test
void verifyValueUsed() {
final int mockedValue = 5;
when(randomExecutor.getThreadCount()).thenReturn(mockedValue);
int result = underTest.getExecutorThreads();
assertThat(result).isEqualTo(mockedValue);
}
#Test
void verifyExecutorCalled() {
underTest.performAction("argument");
verify(randomExecutor).executorMethod("argument");
}
}

How to mock class with #ConfigurationProperties in Spring Boot

I have a class that Autowires another class with #ConfigurationProperties.
Class with #ConfigurationProperties
#ConfigurationProperties(prefix = "report")
public class SomeProperties {
private String property1;
private String property2;
...
Class that Autowires above class SomeProperties
#Service
#Transactional
public class SomeService {
....
#Autowired
private SomeProperties someProperties;
.... // There are other things
Now, I want to test SomeService class and in my test class when I mock SomeProperties class, I am getting null value for all the properties.
Test class
#RunWith(SpringRunner.class)
#SpringBootTest(classes = SomeProperties.class)
#ActiveProfiles("test")
#EnableConfigurationProperties
public class SomeServiceTest {
#InjectMocks
private SomeService someService;
#Mock // I tried #MockBean as well, it did not work
private SomeProperties someProperties;
How can I mock SomeProperties having properties from application-test.properties file.
You are not mocking SomeProperties if you intend to bind values from a properties file, in which case an actual instance of SomeProperties would be provided.
Mock:
#RunWith(MockitoJUnitRunner.class)
public class SomeServiceTest {
#InjectMocks
private SomeService someService;
#Mock
private SomeProperties someProperties;
#Test
public void foo() {
// you need to provide a return behavior whenever someProperties methods/props are invoked in someService
when(someProperties.getProperty1()).thenReturn(...)
}
No Mock (someProperties is an real object that binds its values from some propertysource):
#RunWith(SpringRunner.class)
#EnableConfigurationProperties(SomeConfig.class)
#TestPropertySource("classpath:application-test.properties")
public class SomeServiceTest {
private SomeService someService;
#Autowired
private SomeProperties someProperties;
#Before
public void setup() {
someService = new someService(someProperties); // Constructor Injection
}
...
You need to stub all the property values in #Test/#Before methods of SomeServiceTest.java like :
ReflectionTestUtils.setField(someProperties, "property1", "value1");
ReflectionTestUtils.setField(object, name, value);
You can also mock the reponse for dependent classes via Mockito.when()
If you are using #Mock , you need to stub the values as well. By default the value will be null for all the properties.

How to add a bean in SpringBootTest

The question seems extremely simple, but strangely enough I didn't find a solution.
My question is about adding/declaring a bean in a SpringBootTest, not overriding one, nor mocking one using mockito.
Here is what I got when trying the simplest implementation of my real need (but it doesn't work):
Some service, bean, and config:
#Value // lombok
public class MyService {
private String name;
}
#Value // lombok
public class MyClass {
private MyService monitoring;
}
#Configuration
public class SomeSpringConfig {
#Bean
public MyClass makeMyClass(MyService monitoring){
return new MyClass(monitoring);
}
}
The test:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = { SomeSpringConfig.class })
public class SomeSpringConfigTest {
private String testValue = "testServiceName";
// this bean is not used
#Bean
public MyService monitoringService(){ return new MyService(testValue); }
// thus this bean cannot be constructed using SomeSpringConfig
#Autowired
public MyClass myClass;
#Test
public void theTest(){
assert(myClass.getMonitoring().getName() == testValue);
}
}
Now, if I replace the #Bean public MyService monitoring(){ ... } by #MockBean public MyService monitoring;, it works. I find it strange that I can easily mock a bean, but not simply provide it.
=> So how should I add a bean of my own for one test?
Edit:
I think ThreeDots's answer (create a config test class) is the general recommendation.
However, Danylo's answer (use #ContextConfiguration) fit better to what I asked, i.e. add #Bean directly in the test class.
Spring Test needs to know what configuration you are using (and hence where to scan for beans that it loads). To achieve what you want you have more options, the most basic ones are these two:
Create configuration class outside the test class that includes your bean
#Configuration
public class TestConfig {
#Bean
public MyService monitoringService() {
return new MyService();
}
}
and then add it to to test as configuration class #SpringBootTest(classes = { SomeSpringConfig.class, TestConfig.class })
or
If you only need to use this configuration in this particular test, you can define it in static inner class
public class SomeSpringConfigTest {
#Configuration
static class ContextConfiguration {
#Bean
public MyService monitoringService() {
return new MyService();
}
}
}
this will be automatically recognized and loaded by spring boot test
Simply add the config as
#ContextHierarchy({
#ContextConfiguration(classes = SomeSpringConfig.class)
})
What i am using in this cases is #Import:
#DataJpaTest(showSql = false)
//tests against the real data source defined in properties
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
#Import(value = {PersistenceConfig.class, CustomDateTimeProvider.class})
class MessageRepositoryTest extends PostgresBaseTest {
....
Here i am using a pre configured "test slice".
In this case a need to add my JpaAuditingConfig.
But why not just adding the other beans as you did with your SomeSpringConfig.class ?:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = { SomeSpringConfig.class, OtherBean.class })
public class SomeSpringConfigTest {
...
Everything listed in test will be injectable directly, all not declared must be added as mocks.

Overriding spring #Configuration in an integration test

I have a spring boot configuration class like this:
#Configuration
public class ClockConfiguration {
#Bean
public Clock getSystemClock() {
return Clock.systemUTC();
}
}
and I have some integration tests like this:
#SpringBootTest(classes = MyApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
public abstract class AbstractIntegrationTest {
}
and tests like this:
public class MiscTests extends AbstractIntegrationTest{
#Test
public void CreateSomethingThatOnlyWorksInThe Morning_ExpectCorrectResponse() {
}
I want to be able to offset the clock bean to run some tests at different times on the day. How do I do this?
NOTE: I see several stack overflow answers similar to this, but I can't get them to work.
Based on other responses, it appears the solution should be something like:
#SpringBootTest(classes = MyApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
public abstract class AbstractIntegrationTest {
#Configuration
class MyTestConfiguration {
#Bean
public Clock getSystemClock() {
Clock realClock = Clock.systemDefaultZone();
return Clock.offset(realClock, Duration.ofHours(9));
}
}
}
But nothing happens there. Do I need to #Import something? do I need to #Autowired something?
Thanks!
As you are using Spring Boot you can take advantage of the #MockBean annotation:
#SpringBootTest(classes = MyApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
public abstract class AbstractIntegrationTest {
#MockBean
private Clock clockMock;
}
Then you can stub public methods of that bean an each of the tests accordingly and uniquely:
#Test
public void CreateSomethingThatOnlyWorksInThe Morning_ExpectCorrectResponse() {
when(clockMock.getTime()).thenReturn(..);
}
As per javadoc of #MockBean:
Any existing single bean of the same type defined in the context will
be replaced by the mock.
it is the #TestConfiguration annotation that you need https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/context/TestConfiguration.html
#RunWith(SpringRunner.class)
public class ClockServiceImplIntegrationTest {
#TestConfiguration
static class TestOverridingClockServiceConfiguration {
#Bean
public ClockService clockService() {
return new ClockServiceImpl();
}
}
#Autowired
private ClockService clockService;
#MockBean
private ClockRepository clockRepository;
// write test cases here
}
In the case you have existing configuration you c

Spring not calling #Bean method in tests

I have a Repository MyRepository which is a #Repository. This repository is used by one of my rest controllers. What I want to test is if authorization of my rest controller works properly, thus my tests use #WithUserDetails. I want to mock a call to MyRepository by following this tutorial. When I run my tests I get an exception saying:
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods.
Those methods *cannot* be stubbed/verified.
Mocking methods declared on non-public parent classes is not supported.
2. inside when() you don't call method on mock but on some other object.
Through some debugging I found out that my MockConfig#myRepository method is not being called.
src/main/java/com.example
MyRepository
#Repository
interface MyRepository extends CrudRepository<MyEntity, Long> {}
src/test/java/com.example
MockConfig
#Profile("test")
#Configuration
public class MockConfig
{
#Bean
#Primary
public MyRepository myRepository()
{
return Mockito.mock(MyRepository.class);
}
}
MyTestClass
#ActiveProfiles("test")
#AutoConfigureMockMvc
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class)
#TestExecutionListeners({
DependencyInjectionTestExecutionListener.class
})
class MyTestClass
{
#Autowired
private MockMvc mvc;
#Autowired
private MyRepository myRepository;
#Test
#WithUserDetails("some_user")
public void testWithCorrectPermissions()
{
long entityId = 1;
MyEntity mockReturnValue = new MyEntity();
Mockito.when(myRepository.findOne(entityId)).thenReturn(mockReturnValue);
Mockito.when(myRepository.save(mockReturnValue)).thenReturn(mockReturnValue);
this.mockMvc.perform(post("my/api/path")).andExpect(status().isOk());
}
}
Try with the solution proposed in How to exclude a #Repository from component scan when using Spring Data Rest
Add the following annotation to your test class
#EnableJpaRepositories(excludeFilters = {#ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {MyRepository.class})})
class MyTestClass
(...)
If you want to mock the dependency(eg repository) for your testing class, I would suggest you to use MockitoJUnitRunner.class as SpringRunner.class will initialise the Spring application content, which will cause the testing to be slower and also more dependencies required depending on your project configuration.
So, for your MyTestClass
#RunWith(MockitoJUnitRunner.class)
public class MyTestClass{
#Mock
private MyRepository myRepository;
private MyTest myTest;
#Before
public void setUp() throws Exception {
myTest = new MyTest(myRepository);
}
#Test
public void test(){
...
when(myRepository.get(anyInt()).thenReturn(new MyEntity());
...
}
There is some reference here.
If you insist to test using the current implementation, it might be that the MyRepository was scanned by the Spring Data and the bean was initialised by it. You might want to disable the component scanning as recommended by user2456718.

Categories

Resources