How to add property file in Mock method - Mockito - java

I am trying to write JUnit test cases for an application which is in Java Spring Boot 1.2.5.
I have a service class and I am writing JUnit test cases Using Mockito for the same.
Service Class
class ManualWorkFlowService implements ManualWorkFlowInterface {
#Autowired
private Environment environment;
public void fetchFlowData(Long flowId) {
String config = environment.getProperty("manual.workflow.config");
// Big method body to follow.
}
}
JUnit class
#RunWith(MockitoJUnitRunner.class)
public class LMVendorServiceTest extends BaseTest {
#InjectMocks
private ManualWorkFlowInterface service = new ManualWorkFlowService();
#Test
public void fetchFlowDataTest() throws Exception {
service.fetchFlowData("1234");
}
}
The Junit method calls the fetchFlowData() method, but the environment.getProperty("manual.workflow.config") in original method throws null pointer exception (Because environment property is null)
I have fixed the same like below
#RunWith(MockitoJUnitRunner.class)
public class LMVendorServiceTest extends BaseTest {
#InjectMocks
private ManualWorkFlowInterface service = new ManualWorkFlowService();
#Mock
private Environment mockenvironment;
#Test
public void fetchFlowDataTest() throws Exception {
when(mockenvironment.getProperty("manual.workflow.config")).thenReturn("something");
service.fetchFlowData("1234");
}
}
My doubts are,
Can I configure the same property file which the original
application is using in my Test Class? If yes how?
Is it possible, when I call the original method (fetchFlowData(Long flowId)) from Junit, it automatically takes the application property files configured in original application so that environment.getProperty("manual.workflow.config") works fine without configuring a property file separately in Test Class?
I have tried the below approach but the #Value is not fetching property file value
#RunWith(MockitoJUnitRunner.class)
#PropertySource("file:C:\\properties\\application.properties")
public class LMVendorServiceTest extends BaseTest {
#InjectMocks
private ManualWorkFlowInterface service = new ManualWorkFlowService();
#Mock
private Environment mockenvironment;
#Value("${manual.workflow.config}")
private String property; //Null here
#Test
public void fetchFlowDataTest() throws Exception {
when(mockenvironment.getProperty("manual.workflow.config")).thenReturn(property);
service.fetchFlowData("1234");
}
}

Change:
#Autowired
private Environment environment;
To:
private Environment environment;
#Autowired
void setEnvironment(Environment env) {
// Doesn't need to be public if your service and test are in the same package
this.environment = env;
}
And use Spring's MockEnvironment in your test, adding a PropertySource for whichever properties file you want:
MockEnvironment env = new MockEnvironment();
Resource resource = new ClassPathResource("/my.properties");
Properties props = PropertiesLoaderUtils.loadProperties(resource);
env.getPropertySources().addLast(new PropertiesPropertySource("testProperties", props));
ManualWorkFlowInterface service = new ManualWorkFlowService();
service.setEnvironment(env);

Related

Testing springboot applications using Junits (Test methods for classes which require constructors that were autowired)

I have the following class which I need to test using Junits
#Service
public class MyStorageService {
private final Path fileStorageLocation;
#Autowired
public MyStorageService(FileStorageProperties fileStorageProperties) {
fileStorageLocation = Paths.get(fileStorageProperties.getUploadDir()).toAbsolutePath().normalize();
try {
Files.createDirectories(this.fileStorageLocation);
} catch (Exception ex) {
logger.error("An Internal error occurred when creating directories: {}", ex);
throw new FileStorageException("An Internal error occurred when creating directories", ex);
}
}
public String storeFile(String destination, MultipartFile file) {
//Does some copy and storage operations on file system//
}
}
I have the dependent bean FileStorageProperties as given below which reads application.properties from resources folder to get root directory path:
#ConfigurationProperties(prefix = "file")
public class FileStorageProperties {
private String uploadDir;
public String getUploadDir() {
return uploadDir;
}
public void setUploadDir(String uploadDir) {
this.uploadDir = uploadDir;
}
}
I have sample Junit which I am struggling to complete
#RunWith(SpringRunner.class)
#TestPropertySource(locations = "classpath:test.properties")
#SpringBootTest(classes = {MyStorageServiceTests.TestConfiguration.class})
public class MyStorageServiceTests {
#MockBean
private FileStorageProperties fileStorageProperties;
#InjectMocks
private MyStorageService fileStorageService = new MyStorageService(fileStorageProperties);
#Test
public void testFileLocationCreation() {
//assert file created logic and storeFile method logic//
}
#EnableConfigurationProperties(FileStorageProperties.class)
public static class TestConfiguration {
// nothing
}
}
I need to come up with the correct way to setup my testClass, don't want the logic for the unit test case.
When I try to inject fileStorageProperties into MyStorageService constructor it comes as null.
Which will cause java.lang.NullPointerException wherever fileStorageProperties is used.
I am new to java (barely 1 month exp)
Any insight would be helpful.
USing java 1.8 and SpringJUnit4
I was able to proceed by setting the fields in my class which were expected in my constructer:
#Autowired
private FileStorageProperties fileStorageProperties;
ReflectionTestUtils.setField(fileStorageProperties, "uploadDir", source.getAbsolutePath());
MyStorageService myStorageService = new myStorageService(fileStorageProperties);
Mockito #InjectMocks
Mockito tries to inject mocked dependencies using one of the three approaches, in the specified order.
Constructor Based Injection – when there is a constructor defined for the class, Mockito tries to inject dependencies using the biggest
constructor.
Setter Methods Based – when there are no constructors defined, Mockito tries to inject dependencies using setter methods.
Field Based – if there are no constructors or field-based injection possible, then mockito tries to inject dependencies into the field
itself.
#InjectMocks
private MyStorageService fileStorageService = new MyStorageService(fileStorageProperties);
Replace With
#InjectMocks
private MyStorageService fileStorageService;

Mockito when isn't replacing original method behaviour

I got 2 modules User and Email, both of them have 1 entry point which is a facade, rest is package scoped. The configuration is done in 2 classes
#Configuration
class UserConfiguration {
#Bean
UserFacade userFacade(UserRepository repository, EmailFacade emailFacade) {
return new UserFacade(repository, emailFacade);
}
}
#Configuration
class EmailConfiguration {
#Bean
EmailFacade emailFacade(EmailSender emailSender) {
return new EmailFacade(emailSender);
}
}
Now, I want to write tests that don't require Spring to start. I implemented a simple InMemoryRepository to make this happen
#RunWith(MockitoJUnitRunner.class)
public class RegisterUserTest {
#Mock
private EmailFacade emailFacade = new EmailFacade(new FakeEmailSender());
#InjectMocks
private UserFacade userFacade = new UserConfiguration().userFacade(new InMemoryUserRepository(), emailFacade);
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
}
I need some fake objects to instantiate EmailFacade so I wrote fake implementation
public class FakeEmailSender implements EmailSender {
#Override
public void sendEmail(EmailMessage emailMessage) throws RuntimeException {
}
}
In that scenario, I'm testing User domain, so I want to mock Email anyways.
I wrote a test to check if it works
#Test
public void shouldReturnSendingFailed() {
Mockito.when(emailFacade.sendUserVerificationEmail(Mockito.any())).thenReturn(Either.left(EmailError.SENDING_FAILED));
assertThat(userFacade.registerNewUser(RegisterUserDto.builder()
.username(USERNAME_4)
.email(VALID_EMAIL)
.password(VALID_PASSWORD).build()).getLeft(), is(EmailError.SENDING_FAILED));
}
But it isn't... after running this test I got
java.util.NoSuchElementException: getLeft() on Right
edit#
regiserNewUser() method
Either<DomainError, SuccessMessage> register(RegisterUserDto registerUserDto) {
if(userRepository.findUser(registerUserDto.getUsername()).isPresent())
return Either.left(UserError.USERNAME_ALREADY_EXISTS);
var userCreationResult = User.createUser(registerUserDto);
var savedUser = userCreationResult.map(this::saveUser);
var emailDto = savedUser.map(this::createVerificationEmail);
return emailDto.isRight() ? emailFacade.sendUserVerificationEmail(emailDto.get())
: Either.left(emailDto.getLeft());
}
Edit2#
With following test configuration
#RunWith(MockitoJUnitRunner.class)
public class RegisterUserTest {
#Mock
private EmailFacade emailFacade;
#InjectMocks
private UserFacade userFacade = new UserConfiguration().userFacade(new InMemoryUserRepository(), emailFacade);
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
}
I got nullpointer here, last line of registerNewUser().
Try running this code
#RunWith(MockitoJUnitRunner.class)
public class RegisterUserTest {
#Mock
private EmailFacade emailFacade;
private UserFacade userFacade;
#Before
public void setUp() {
userFacade = new UserConfiguration().userFacade(new InMemoryUserRepository(), emailFacade);
}
}
There are a few issues with your code:
You initialize your mocks twice. You don’t need to call initMocks in the setUp method if you are using Mockito runner
You are trying to inject mocks to already initialized object. But the field you are trying to inject is also passed to the constructor. Please read #InjectMocks doc, to check the strategies used to inject the mocks:
constructor (not used here, already initialized object)
setter (do you have one?)
field (is it not final)
There are details to each strategy (see my questions above). If no staregy is matched, Mockito will fail silently. The fact that you are passing an object in constructor, and rely on setter or field injection afterwards makes this code unnecesarily complex.

Mock embedded objects in integration test

Trying to write an integration test for a Spring application. Say i've got a class A which contains a class B object. Class B contains a class C object and I need to mock an object within this class for the integration test - any idea how i go about doing that without passing every object through as a parameter in the constructor?
e.g.
#Service
Class A {
#Autowired
private B b;
public void testA() {
B.testB();
}
}
#Service
Class B {
#Autowired
private C c;
public void testB() {
c.testC();
}
}
#Service
Class C {
//External class pulled in from dependency library
#Autowired
private RestTemplate restTemplate;
public void testC() {
restTemplate.doSomethingInOutsideWorld();
}
}
Integration test:
#RunWith(JUnitParamsRunner.class)
#SpringBootTest
public class MyIt {
#ClassRule
public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule();
#Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
#Mock
private RestTemplate restTemplate;
#Autowired
private A a;
#InjectMocks
private C c;
#Before
public void setup() {
initMocks(this);
}
#Test
public void test1() throws IOException {
a.testA()
}
}
Doesn't mock the RestTemplate object, it tries to hit the outside world. Any advice on how to resolve this?
Achieve this by using SpringRunner and #MockBean
#RunWith(SpringRunner.class) is used to provide a bridge between Spring Boot test features and JUnit. Whenever we are using any Spring Boot testing features in out JUnit tests, this annotation will be required.
The #SpringBootTest annotation can be used when we need to bootstrap the entire container. The annotation works by creating the ApplicationContext that will be utilized in our tests.
Annotation that can be used to add mocks to a Spring ApplicationContext. Can be used as a class level annotation or on fields in either #Configuration classes, or test classes that are #RunWith the SpringRunner.
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyIt {
#MockBean
private RestTemplate restTemplate;
#Autowired
private A a;
#Before
public void setup() {
initMocks(this);
}
#Test
public void test1() throws IOException {
given(this.restTemplate.doSomethingInOutsideWorld()).willReturn(custom object);
a.testA()
}
}

How to read spring boot property file in service class method called by unit test

I have spring boot application. I want to write some unit test for methods in service class.
I can load Environment variable and get properties in unit test class but can't do it in service class. Environment in service class is always null when reaching it from unit tests. It work when reaching it from application.
SomethingServiceTest.java
#RunWith(SpringRunner.class)
#DataJpaTest
#TestPropertySource(value = "/application.properties")
public class SomethingServiceTest {
private ISomethingService m_SomethingService;
#PostConstruct
public void setup() {
m_SomethingService = new SomethingService();
m_SomethingService.setSomethingRepository(somethingRepository);
// somethingRepository is mocked class, probably not important
}
#Test
public void test_somethingMethod() {
System.out.println(env.getProperty("some.property"));
//env here is full and i get wanted property
m_uploadService.doSomething();
}
ISomethingService.java
public interface ISomethingService {
doSomething();
}
SomethingService.java
#Service
public class SomethingService implements ISomethingService {
#Value("${some.property}")
private String someProperty;
private ISomethingRepository somethingRepository;
#Autowired
public ISomethingRepository getSomethingRepository() {
return somethingRepository;
}
public void setSomethingRepository(ISomethingRepository p_somethingRepository) {
somethingRepository = p_somethingRepository;
}
#Autowired
private Environment env;
#Override
#Transactional
public String doSomething(){
System.out.println(env.getProperty("some.property"));
//env is null here
return someProperty;
}
}

Spring/TestNG integration: Auto-injection fails with two test classes

Hitting an odd issue with my spring based integration tests. Note that I'm not so much unit testing a spring IoC app as I'm using Spring to auto-inject properties into my test configuration. Also - we're not using any context configuration xml, everything is configured programatically.
I got the framework all set up and running with a single test before handing it off to the team tester, auto-injection was working fine. However, as soon as he added a second test class, auto-injection of my configuration field just stops working. I've cloned his branch and verified the behavior; Commenting out his class OR my original class causes the auto-injection to return to normal function.
Anybody ever seen this? Relevant code snippets below. I've tried moving around the #ContextConfiguration annotation but it didn't really help. It seems like Spring is having trouble deciding where the TestProperties object should come from. The field I've commented on below is coming up null when I run the tests, but only if there are two test classes enabled.
#ContextConfiguration(classes = TestConfig.class, loader = AnnotationConfigContextLoader.class)
public abstract class BaseIntegrationTest
extends AbstractTestNGSpringContextTests
{
#Autowired
protected MockServerClient mockServer;
// I get an error in IntelliJ Pro on this field.
// Could not autowire. There is more than one bean of 'TestProperties' type. Beans: getTestProperties, testProperties
// I've also tried #Qualifier("getTestProperties") and #Qualifier("testProperties")
// which both resolve the error in IntelliJ but the TestProperties field is still null when running the tests.
#Autowired
protected TestProperties properties;
//...
}
public class TestGetMuIT
extends BaseIntegrationTest
{
private ManagementUnitClient managementUnitClient;
#BeforeMethod(alwaysRun = true)
public void setup()
{
String endpoint = properties.getServiceBaseUri(); //NullPointerException thrown here during test run
}
}
Other relevant classes:
#Configuration
public class RootConfig
{
protected static final Logger LOGGER = LoggerFactory.getLogger(RootConfig.class);
private PropertyService propertyService;
private TestProperties testProperties;
#Bean
public PropertyService propertyService() throws IOException {
if (propertyService == null) {
propertyService = new DynamoDbPropertyService(config.getPropertiesConfig());
propertyService.initialize();
}
return propertyService;
}
#Bean
public TestProperties getTestProperties() throws IOException {
if (testProperties == null) {
testProperties = new TestProperties();
}
}
And the actual TestProperties class:
#Component
public class TestProperties
{
public static class Default {
public static final String BASE_URI = "http://localhost:8055";
}
#Autowired
private PropertyService propertyService;
public String getServiceBaseUri()
{
return propertyService.getPropertyRegistry().getString(TestConfigKey.LocalServiceBaseUri.key(), Default.BASE_URI);
}
}
And these are my spring config annotations:
#Configuration
//Note: TestProperties.class is a recent addition. I added that and commented out the #Bean getTestProperties() method in RootConfig as a troubleshooting step
#Import({RootConfig.class, TestProperties.class})
#EnableAspectJAutoProxy
#ComponentScan(basePackages = {"com.inin.wfm.it"},
excludeFilters = #ComponentScan.Filter(value = com.inin.wfm.it.config.ConfigPackageExcludeFilter.class, type = FilterType.CUSTOM))
public class TestConfig {\\... }
Again all of the above works just fine if there's only one test file (and it doesn't matter which of the two I comment out). Anybody know what I'm doing wrong in my configuration?

Categories

Resources