I have a spring project in Java.
In the project, I have a Kafka configuration class and an additional class that uses the Kafka templets and performs some logic.
I am trying to create a unit test to verify my logic but I am not able to #Autowire the class in the test class.
Here are my classes:
Kafka configuration
#Configuration
#Getter
#PropertySource(value = "classpath:realtime.properties")
public class KafkaProducerConfig {
#Value("${kafka.producer.client.id}")
private String producerClientId;
#Value("${kafka.url}")
private String kafkaUrl;
#Value("${kafka.retries.config}")
private int retryConfig;
#Bean
public ProducerFactory<Void, SomeClass1> producerFactory1() {
Map<String, Object> configProps = new HashMap<>();
configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaUrl);
configProps.put(ProducerConfig.CLIENT_ID_CONFIG, producerClientId.concat("_someClass1"));
configProps.put(ProducerConfig.RETRIES_CONFIG, retryConfig);
configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, VoidSerializer.class.getName());
configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, AvroSerde.serializerFor(SomeClass1.class));
return new DefaultKafkaProducerFactory<>(configProps);
}
#Bean
public KafkaTemplate<Void, SomeClass1> kafkaTemplate1() {
return new KafkaTemplate<>(producerFactory1());
}
#Bean
public ProducerFactory<Void, someClass2> producerFactory2() {
Map<String, Object> configProps = new HashMap<>();
configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaUrl);
configProps.put(ProducerConfig.CLIENT_ID_CONFIG, producerClientId.concat("_someClass2"));
configProps.put(ProducerConfig.RETRIES_CONFIG, retryConfig);
configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, VoidSerializer.class.getName());
configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, AvroSerde.serializerFor(someClass2.class));
return new DefaultKafkaProducerFactory<>(configProps);
}
#Bean
public KafkaTemplate<Void, someClass2> kafkaTemplate2() {
return new KafkaTemplate<>(producerFactory2());
}
}
The class who uses Kafka configuration:
#Component
public class FilterPublisher {
private final String SERVICE_NAME = "some-service";
#Value("${kafka.notification.topic}")
private String notificationTopic;
#Value("${kafka.tracing.topic}")
private String tracingTopic;
#Autowired
private KafkaTemplate<Void, someClass1> kafkaTemplate1;
#Autowired
private KafkaTemplate<Void, someClass2> kafkaTemplate2;
public FilterPublisher() {
}
public void doSomeLogic() {
// do something ...
}
private void publishToKafka() {
// do something ....
}
}
The unit test class:
#TestPropertySource(locations = "classpath:application-test.properties")
#SpringBootTest(classes = {FilterPublisher.class, KafkaProducerConfig.class})
public class FilterPublisherTest {
#Autowired
private FilterPublisher filterPublisher;
#Test
public void tmpTest() {
filterPublisher.processConfigurations();
}
}
When running the test, filterPublisher is always null.
What am I missing? what am I doing wrong? how do I fix it?
I also tried using the import annotation on the test class #Import(TestConfig.class)
#TestConfiguration
public class TestConfig {
#Bean
public FilterPublisher filterPublisher() {
return new FilterPublisher();
}
}
but the result was the same.
Any help would be appreciated, thank you.
Finally got it working,
here is the code for the Test class:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {FilterPublisher.class, KafkaProducerConfig.class})
public class FilterPublisherTest {
#Autowired
ApplicationContext context;
#Autowired
FilterPublisher filterPublisher;
// ALL TEST METHODS GO HERE
}
Related
This is my first time working with Externalized Configuration and yaml.
I created a yaml where I use the class name as KEY, and the field name as VALUE
YAML:
project:
test:
service:
computator:
# exclude field from beeing randomly valorized
population:
exclude:
InputClass: 'myDate'
AnotherClass: 'myName'
ExcludePopulationProperties:
#Data
#Component
#ConfigurationProperties(prefix = "project")
public class ExcludePopulationProperties {
private Test test;
#Data
public static class Test {
private Service service;
}
#Data
public static class Service {
private Computator computator;
}
#Data
public static class Computator {
private Population population;
}
#Data
public static class Population {
private Map<String, String> exclude;
}
}
Test with JUnit 5:
#ContextConfiguration(classes = { ExcludePopulationProperties.class })
#ExtendWith(SpringExtension.class)
class YamlTest {
#Autowired
private ExcludePopulationProperties excludePopulationProperties;
#Test
void testExternalConfiguration() {
Map<String, String> map = excludePopulationProperties.getTest().getService().getComputator().getPopulation().getExclude();
assertNotNull(map);
}
The problem is that I have a NullPointerException because test is null
So I'm not sure what is wrong here, I was expecting that the map was correctly populated.
I also tried to add
#TestPropertySource(properties = { "spring.config.location=classpath:application-_test.yaml" })
on the YamlTest
Your ExcludePopulationProperties class should not be annotated with #Component. Instead, you should have the annotation #EnableConfigurationProperties(ExcludePopulationProperties.class) on a configuration class in your project (the main application class will work).
Change your properties class to look like this (removing #Component):
#Data
#ConfigurationProperties(prefix = "project")
public class ExcludePopulationProperties {
...
}
Change your application class to enable the configuration properties:
#SpringBootApplication
#EnableConfigurationProperties(ExcludePopulationProperties.class)
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
And the YamlTest class to look like this (using #SpringBootTest):
#SpringBootTest
#ContextConfiguration(classes = { DemoApplication.class })
#TestPropertySource(properties = { "spring.config.location=classpath:application-test.yaml" })
class YamlTest {
#Autowired
private ExcludePopulationProperties excludePopulationProperties;
#Test
void testExternalConfiguration() {
Map<String, String> map = excludePopulationProperties.getTest().getService().getComputator().getPopulation().getExclude();
assertNotNull(map);
assertEquals(map.get("InputClass"), "myDate");
assertEquals(map.get("AnotherClass"), "myName");
}
}
With these little changes, now I'm able to test the properties from YAML file.
I improved the yaml a little bit:
# test placeholders
project:
test:
service:
computator:
# exclude field from beeing randomly valorized
population:
exclude:
InputClass:
- 'myDate'
AnotherClass:
- 'myName'
so now the ExcludePopulationProperties have a Map<String, List<String>> instead of Map<String, String>, in this way I will be able to exclude more than one field from the same class:
#Data
#Configuration
#ConfigurationProperties(prefix = "project")
#PropertySource(value = "classpath:application-_test.yaml", factory = YamlPropertySourceFactory.class)
public class ExcludePopulationProperties {
private Test test;
#Data
public static class Test {
private Service service;
}
#Data
public static class Service {
private Computator computator;
}
#Data
public static class Computator {
private Population population;
}
#Data
public static class Population {
private Map<String, List<String>> exclude;
}
}
YamlPropertySourceFactory is a class implemented by Baeldung in this guide:
#PropertySource with YAML Files in Spring Boot
public class YamlPropertySourceFactory implements PropertySourceFactory {
#Override
public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(resource.getResource());
Properties properties = factory.getObject();
return new PropertiesPropertySource(resource.getResource().getFilename(), properties);
}
}
Test Class :
#EnableConfigurationProperties
#ContextConfiguration(classes = { ExcludePopulationProperties.class })
#TestPropertySource(properties = { "spring.config.location=classpath:application-_test.yaml" })
#ExtendWith(SpringExtension.class)
class YamlTest {
#Autowired
private ExcludePopulationProperties excludePopulationProperties;
#Test
void testExternalConfiguration() {
Map<String, List<String>> map = excludePopulationProperties.getTest().getService().getComputator().getPopulation().getExclude();
assertNotNull(map);
}
}
Please note that for Mockito you need to use both, SpringExtension and MockitoExtension:
#EnableConfigurationProperties
#ContextConfiguration(classes = { ExcludePopulationProperties.class })
#Extensions({
#ExtendWith(SpringExtension.class),
#ExtendWith(MockitoExtension.class)
})
class YamlTest {
}
UPDATE
I find a better solution in order to avoid writing the annotations on all test classes.
add the jackson jackson-dataformat-yaml dependency
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>${jackson-dataformat-yaml.version}</version>
</dependency>
The configuration properties class will be:
#Data
public class ExcludePopulationProperties {
private Project project;
#Data
public static class Project {
private Test test;
}
#Data
public static class Test {
private Service service;
}
#Data
public static class Service {
private Computator computator;
}
#Data
public static class Computator {
private Population population;
}
#Data
public static class Population {
private Map<String, List<String>> exclude;
}
public static ExcludePopulationProperties build() throws IOException {
InputStream inputStream = new FileInputStream(new File("./src/test/resources/" + "application-_test.yaml"));
YAMLMapper mapper = new YAMLMapper();
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
mapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
return mapper.readValue(inputStream, ExcludePopulationProperties.class);
}
}
then, wherever you need, simply call the static build method, the test class will be more simple :
#ExtendWith(SpringExtension.class)
class YamlTest {
#Test
void testExternalConfiguration() throws IOException {
Map<String, List<String>> map = ExcludePopulationProperties.build().getProject().getTest().getService().getComputator().getPopulation().getExclude();
assertNotNull(map);
}
}
I have a junit test method as follows:
#SpringBootTest
public class StoreIdAssignmentServiceTest {
private static final Logger log = LoggerFactory
.getLogger(StoreIdAssignmentServiceTest.class);
#InjectMocks
private StoreIdAssignmentService storeIdAssignmentService;
#Mock
private StoreIdAssignmentFactory storeIdAssignmentFactory;
#Mock
private DatabaseService databaseService;
#Test
public void rollUpFeed_Single_DealerAndStoreID_NoExisting() {
List<ScmsaPosTransRollup> scmsaPosTransRollupFeedList = new ArrayList<>();
ScmsaPosTransRollup posTransRollup = new ScmsaPosTransRollup();
posTransRollup.setJobLogId(8269726L);
posTransRollup.setDealerCode("3119255");
posTransRollup.setStoreId("9842");
posTransRollup.setTransactionDate(Timestamp
.valueOf("2018-03-01 13:00:00.00"));
posTransRollup.setQuantity(4);
posTransRollup.setRollupType("H");
scmsaPosTransRollupFeedList.add(posTransRollup);
Mockito.when(
databaseService.getUnProcessedRollUpFeedBasedonRollupType("H"))
.thenReturn(scmsaPosTransRollupFeedList);
List<PosHourlySt> existingPosHourlyStEntries = new ArrayList<>();
Mockito.when(databaseService.getDealerCodeFromPosHourly("3119255"))
.thenReturn(existingPosHourlyStEntries);
Mockito.when(databaseService.getDealerCodeFromPosHourly("3119255"))
.thenReturn(existingPosHourlyStEntries);
storeIdAssignmentService.processHourlyStateFeed();
assertNotNull(posHourlyStRepository.findAll());
}
}
And My StoreIdAssignmentService class will be:
#Service
public class StoreIdAssignmentService {
private StoreIdAssignmentFactory storeIdAssignmentFactory;
private DatabaseService databaseService;
#Autowired
public StoreIdAssignmentService(StoreIdAssignmentFactory storeIdAssignmentFactory,
DatabaseService databaseService) {
this.storeIdAssignmentFactory = storeIdAssignmentFactory;
this.databaseService = databaseService;
}
public void processHourlyStateFeed() {
.......................
calculateStateForPosHourlyStTransaction(posHourlyStToConsider, newPosHourlyStEntries);
.........
}
List<ScmsaPosTransRollup> scmsaPosTransRollupUpdatedFlagList = storeIdAssignmentFactory
.createUpdatedRollUpEntries(rollUpFeedByDealerCode);
saveAndUpdatePosHourlyStAndRollUpEntries(newPosHourlyStEntries, existingPosHourlyStEntries,
rollUpFeedByDealerCode, scmsaPosTransRollupUpdatedFlagList);
}
}
private Map<String, List<ScmsaPosTransRollup>> groupDealerCodeRollUpFeedByStoreId(
List<ScmsaPosTransRollup> rollUpFeedByDealerCode) {
// Grouping the rollUpFeedByDealerCode by storeID
return rollUpFeedByDealerCode.stream().collect(Collectors.groupingBy(ScmsaPosTransRollup::getStoreId));
}
private void calculateStateForPosHourlyStTransaction(ScmsaPosTransRollup scmsaPosTransRollupToConsider, List<PosHourlySt> newPosHourlyStEntries) {
List<PosHourlySt> posHourlyStList = newPosHourlyStEntries.stream().filter(
hourlyState -> (hourlyState.getStartDate().before(scmsaPosTransRollupToConsider.getTransactionDate())))
.collect(Collectors.toList());
..............
PosHourlySt posHourlySt=storeIdAssignmentFactory.createHourlyStEntryFromRollUp(scmsaPosTransRollupToConsider,
Timestamp.valueOf(scmsaPosTransRollupToConsider.getTransactionDate().toLocalDateTime().withHour(0).withMinute(0)),
Timestamp.valueOf(scmsaPosTransRollupToConsider.getTransactionDate().toLocalDateTime().withHour(23).withMinute(59)));
newPosHourlyStEntries.add(posHourlySt);
....................
}
}
and My Factory class would be:
#Component
public class StoreIdAssignmentFactory {
private static final Logger log = LoggerFactory.getLogger(StoreIdAssignmentFactory.class);
private ModelMapper modelMapper;
#Autowired
public StoreIdAssignmentFactory(ModelMapper modelMapper) {
this.modelMapper = modelMapper;
}
public PosHourlySt createHourlyStEntryFromRollUp(ScmsaPosTransRollup scmsaPosTransRollup, Timestamp startDate, Timestamp endDate){
PosHourlySt posHourlySt = new PosHourlySt();
posHourlySt.setDealerCode(scmsaPosTransRollup.getDealerCode());
posHourlySt.setSourceJobLogId(scmsaPosTransRollup.getJobLogId());
posHourlySt.setStartDate(startDate);
posHourlySt.setStoreId(scmsaPosTransRollup.getStoreId());
posHourlySt.setEndDate(endDate);
posHourlySt.setJobLogId(0L);
posHourlySt.setSource("ROLLUP");
log.info("New Rec: {}", posHourlySt.toString());
return posHourlySt;
}
public PosHourlySt createHourlyStEntryFromPosHourlySt(PosHourlySt posHourlyStToSplit, Timestamp endDate){
PosHourlySt posHourlySt = new PosHourlySt();
posHourlySt.setDealerCode(posHourlyStToSplit.getDealerCode());
posHourlySt.setSourceJobLogId(posHourlyStToSplit.getJobLogId());
posHourlySt.setStartDate(posHourlyStToSplit.getStartDate());
posHourlySt.setStoreId(posHourlyStToSplit.getStoreId());
posHourlySt.setEndDate(endDate);
posHourlySt.setJobLogId(0L);
posHourlySt.setSource("ROLLUP");
log.info("SplitupRec: {}", posHourlySt.toString());
return posHourlySt;
}
public List<ScmsaPosTransRollup> createUpdatedRollUpEntries(List<ScmsaPosTransRollup> rollUpFeedByDealerCode) {
List<ScmsaPosTransRollup> scmsaPosTransRollupUpdatedFlagList = new ArrayList<>();
for(ScmsaPosTransRollup scmsaPosTransRollupFeed : rollUpFeedByDealerCode) {
ScmsaPosTransRollup scmsaPosTransRollupUpdateFlag = new ScmsaPosTransRollup();
modelMapper.map(scmsaPosTransRollupFeed, scmsaPosTransRollupUpdateFlag);
scmsaPosTransRollupUpdateFlag.setProcessedFlag("Y");
scmsaPosTransRollupUpdatedFlagList.add(scmsaPosTransRollupUpdateFlag);
}
return scmsaPosTransRollupUpdatedFlagList;
}
}
The StoreIdAssignmentService class contains the method "calculateStateForPosHourlyStTransaction" which calls some method in Factory class. When I debug as the junit test case , am not able to call that factory class method . What I am doing wrong here. Can anyone please suggest me.
You are mocking the factory:
#Mock
private StoreIdAssignmentFactory storeIdAssignmentFactory;
So you can't investigate the method createHourlyStEntryFromRollUp inside the factory, because the whole factory is mocked:
storeIdAssignmentFactory.createHourlyStEntryFromRollUp
If you trying to debug the createHourlyStEntryFromRollUp and the StoreIdAssignmentFactory is a #Component (or #Service), I recommend create a StoreIdAssignmentFactoryTest class test, use the #Autowired on it and #MockBean his dependencies.
Example:
#SpringBootTest
public class StoreIdAssignmentFactoryTest {
#Autowired
StoreIdAssignmentFactory factory;
#Test
public void testing() {
List<ScmsaPosTransRollup> list = factory.createHourlyStEntryFromRollUp(...);
//TODO asserts and etc
}
}
But this test is considered a integration test, because it load the whole spring context and beans related.
Another alternative is the (true) unit test. Use your constructor and not use #SpringBootTest, mock the dependencies (ModelMapper). This makes the test more fast and simple.
Example:
public class StoreIdAssignmentFactoryTest {
#Test
public void testing() {
ModelMapper mapper = mock(ModelMapper.class);
StoreIdAssignmentFactory factory = new StoreIdAssignmentFactory(mapper)
List<ScmsaPosTransRollup> list = factory.createHourlyStEntryFromRollUp();
//TODO asserts and etc
}
}
Hello everyone I wanted to tested the full validation of a Request in my Spring Boot application I mean no testing one validator at a time but all of them on the target object)
First I have my object :
public class UserCreationRequest {
#JsonProperty("profileId")
#NotNull
#ValidProfile
private Integer profileId;
}
Then my Validator (#ValidProfile):
#Component
public class ProfileValidator implements ConstraintValidator<ValidProfile, Integer> {
#Autowired
private IProfileService profileService;
#Autowired
private IUserRestService userRestService;
#Override
public void initialize(ValidProfile constraintAnnotation) {
}
#Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
RestUser restUser = userRestService.getRestUser();
ProfileEntity profileEntity = profileService.getProfile(value, restUser.getAccountId());
return profileEntity != null;
}
}
Now I write my unit test :
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {ValidationTestConfiguration.class})
public class UserCreationRequestValidationTest {
private static LocalValidatorFactoryBean localValidatorFactory;
#Autowired
private IUserService userService;
#Autowired
private IProfileService profileService;
#Autowired
private IUserRestService restService;
#BeforeClass
public static void createValidator() {
localValidatorFactory = new LocalValidatorFactoryBean();
localValidatorFactory.setProviderClass(HibernateValidator.class);
localValidatorFactory.afterPropertiesSet();
}
#AfterClass
public static void close() {
localValidatorFactory.close();
}
#Test
public void validateUserCreationRequestStringfields() {
UserCreationRequest userCreationRequest = new UserCreationRequest();
/* Here fill test object*/
when(userService.getUser(any(Integer.class), any(Integer.class))).thenReturn(new UserEntity());
when(profileService.getProfile(any(Integer.class), any(Integer.class))).thenReturn(new ProfileEntity());
when(restService.getRestUser()).thenReturn(new RestUser());
Set<ConstraintViolation<UserCreationRequest>> violations
= localValidatorFactory.validate(userCreationRequest);
assertEquals(violations.size(), 8);
}
}
and my TestConfiguration is like that :
#Configuration
public class ValidationTestConfiguration {
#Bean
#Primary
public IProfileService profileService() {
return Mockito.mock(IProfileService.class);
}
#Bean
#Primary
public IUserRestService userRestService() { return Mockito.mock(IUserRestService.class); }
}
On execution I can see that in the test itself the injection works :
restService is mapped to "Mock for IUserRestService"
But in my validator it is not injected, userRestService is null.
Same thing for ProfileService
I tried several things seen here, nothing works (code is running, only test conf is failing)
This is because you do not produce the Validator bean so it can be injected.
As you manually instantiate the LocalValidatorFactoryBean, it cannot access to the spring DI defined for this test.
You should produce instead a bean for the Validator, or even reference an existing spring configuration to do so.
I have an application which needs a service to be Spring wired in a JsonDeserializer. The problem is that when I start up the application normally it is wired, but when I start it up in a test, it is null.
The relevant code is:
JSON Serializer/Deserializer:
#Component
public class CountryJsonSupport {
#Component
public static class Deserializer extends JsonDeserializer<Country> {
#Autowired
private CountryService service;
#Override
public Country deserialize(JsonParser jsonParser, DeserializationContext ctx) throws IOException {
return service.getById(jsonParser.getValueAsLong());
}
}
}
Domain Object:
public class BookingLine extends AbstractEntity implements TelEntity {
.....other fields
//Hibernate annotations here....
#JsonDeserialize(using = CountryJsonSupport.Deserializer.class)
private Country targetingCountry;
..... other fields
}
Test Class:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(Application.class)
#WebIntegrationTest({"server.port=0"})
#ActiveProfiles("test")
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class BookingAndLinesControllerFunctionalTest {
#Test
public void testGetBooking() {
Booking booking = bookingRepositoryHelper.createBooking();
bookingRepository.save(booking);
String uri = String.format("http://localhost:%s/api/v1/booking-and-lines/" + booking.getBookingCode(), port);
Booking booking1 = restTemplate.getForObject(uri, Booking.class); // line which falls over because countryService is null
}
}
Any ideas?
Managed to discover the answer to this one after fiddling around long enough. Just needed some config like this:
#Configuration
#Profile("test")
public class TestConfig {
#Bean
public HandlerInstantiator handlerInstantiator() {
return new SpringHandlerInstantiator(applicationContext.getAutowireCapableBeanFactory());
}
#Bean
public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder(HandlerInstantiator handlerInstantiator) {
Jackson2ObjectMapperBuilder result = new Jackson2ObjectMapperBuilder();
result.handlerInstantiator(handlerInstantiator);
return result;
}
#Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(Jackson2ObjectMapperBuilder objectMapperBuilder) {
return new MappingJackson2HttpMessageConverter(objectMapperBuilder.build());
}
#Bean
public RestTemplate restTemplate(MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter) {
List<HttpMessageConverter<?>> messageConverterList = new ArrayList<>();
messageConverterList.add(mappingJackson2HttpMessageConverter);
return new RestTemplate(messageConverterList);
}
}
I have a rest resource for signup and login. both in a controller class. the controller class has a dependency to a service class with the business logic. the service class has further dependencies. cause i use an embedded db for testing, i want to use the real dependencies of my app instead to mock them with something like #injectmock #mock. there is only one certain dependency i have to mock. its the dependency for sending emails after a signup process. how to write test cases with #autowired function and one certain mock dependency for email notification?
#Controller
public class AccountCommandsController {
#Autowired
private LogoutService service;
#RequestMapping(value = "/rest/login", method = RequestMethod.POST)
public ResponseEntity login(#RequestBody Account account) {
AccountLoginEvent accountLoginEvent = service.loginAccount(new RequestAccountLoginEvent(account.getEmailAddress(), account.getPassword()));
if (accountLoginEvent.isLoginGranted()) {
return new ResponseEntity(HttpStatus.ACCEPTED);
} else {
return new ResponseEntity(HttpStatus.UNAUTHORIZED);
}
}
#RequestMapping(value = "/rest/signup", method = RequestMethod.POST)
public ResponseEntity signup(#RequestBody Account account) {
AccountSignupEvent signedupEvent = service.signupAccount(new RequestAccountSignupEvent(account.getEmailAddress(), account.getPassword()));
if (signedupEvent.isSignupSuccess()) {
return new ResponseEntity(HttpStatus.ACCEPTED);
} else if (signedupEvent.isDuplicateEmailAddress()) {
return new ResponseEntity(HttpStatus.CONFLICT);
} else if (signedupEvent.isNoSignupMailSent()) {
return new ResponseEntity(HttpStatus.SERVICE_UNAVAILABLE);
} else {
return new ResponseEntity(HttpStatus.FORBIDDEN);
}
}
}
#Service
public class LogoutService {
#Autowired
private AccountsRepository accountsRepository;
#Autowired
private MailService mailService;
#Autowired
private HashService hashService;
public AccountSignupEvent signupAccount(RequestAccountSignupEvent signupEvent) {
if (accountsRepository.existEmailAddress(signupEvent.getEmailAddress())) {
return AccountSignupEvent.duplicateEmailAddress();
}
Account newAccount = new Account();
newAccount.setCreated(new Date());
newAccount.setModified(new Date());
newAccount.setEmailAddress(signupEvent.getEmailAddress());
newAccount.setPassword(signupEvent.getPassword());
newAccount.setVerificationHash(hashService.getUniqueVerificationHash());
SignupMailEvent mailSentEvent = mailService.sendSignupMail(new RequestSignupMailEvent(newAccount));
if (!mailSentEvent.isMailSent()) {
return AccountSignupEvent.noMailSent();
}
Account persistedAccount = accountsRepository.persist(newAccount);
return AccountSignupEvent.accountCreated(persistedAccount);
}
public AccountLoginEvent loginAccount(RequestAccountLoginEvent loginEvent) {
if (accountsRepository.existLogin(loginEvent.getEmailAddress(), loginEvent.getPassword())) {
return AccountLoginEvent.granted();
}
return AccountLoginEvent.denied();
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = TestConfiguration.class)
#Transactional
#TransactionConfiguration(defaultRollback = true)
public class LogoutTest {
private MockMvc mockMvc;
#Autowired
private AccountCommandsController controller;
#Before
public void setup() {
mockMvc = standaloneSetup(controller).build();
}
#Test
public void signupNoMail() throws Exception {
doReturn(AccountSignupEvent.noMailSent()).when(service).signupAccount(any(RequestAccountSignupEvent.class));
// when(controller.service.signupAccount(any(RequestAccountSignupEvent.class))).thenReturn(AccountSignupEvent.noMailSent());
mockMvc.perform(post("/rest/signup")
.content(new Gson().toJson(new Account(UUID.randomUUID().toString(), UUID.randomUUID().toString())))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isServiceUnavailable());
}
}
I hope you see the problem. Every dependency works fine instead mailservice. I dont want to use #injectmock and #mock with MockitoAnnotations.initMocks(this); in my test file, because of the neccessary to provide for all dependencies mocks.
if your dependencies are running and you have a configuration class where you have defined the endpoint, you can use ConfigurableApplicationContext class, something like this:
public class test {
private static ConfigurableApplicationContext appContext;
private LogoutService service;
#AfterClass
public static void destroy() {
appContext.close();
}
#Before
public void setup() {
appContext = new AnnotationConfigApplicationContext(YourClassConfig.class);
service = appContext.getBean(LogoutService.class);
}
#Test
public void beansAreCreated() {
assertNotNull(service);
}
}
Or you can re-write your endpoint with a configuration class and you can use WireMock (http://wiremock.org) to emulate your dependency with real data, this should be something like this:
public class test {
#Rule
public WireMockRule wireMockRule = new WireMockRule(15000);
private static ConfigurableApplicationContext appContext;
private LogoutService service;
private static String serviceMockUrl;
#AfterClass
public static void destroy() {
appContext.close();
}
#Before
public void setup() {
serviceMockUrl = "http://localhost:" + wireMockRule.port();
appContext = new AnnotationConfigApplicationContext(TestConfig.class);
stubFor(get(urlEqualTo("urlToRequest")).
willReturn(aResponse().
withStatus(SC_OK).
withBody(createJsonArray("MapWithYourData").
withHeader("Content-Type", "application/json")));
service = appContext.getBean(LogoutService.class);
}
#Test
public void beansAreCreated() {
assertNotNull(service);
}
#Configuration
static class TestConfig {
#Bean
public PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new PropertyPlaceholderConfigurer() {{
setProperties(new Properties() {{
setProperty("service.url", serviceMockUrl);
}});
}};
}
}
}
I hope this help you.
What you are trying to do is easily implemented using Spring Profiles.
On way to achieve it is the following:
#Configuration
public class TestConfiguration {
//this is the real mail service
#Bean
public MailService mailService() {
return new MailService(); //or whatever other bean creation logic you are using
}
//whatever else
}
#Configuration
#Profile("mockMail")
public class MockMailServiceConfig {
#Bean
#Primary
public MailService mockMailService() {
return mock(MailService.class);
}
}
Your test class would then look like:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = TestConfiguration.class)
#Transactional
#TransactionConfiguration(defaultRollback = true)
#ActiveProfiles("mockMail")
public class LogoutTest {
//do your testing
}
Note the use of #Primary in MockMailServiceConfig. I opted for this way since it wouldn't require you to introduce profiles anywhere else if you are not already using them. #Primary tells spring to use that specific bean if multiple candidates are available (in this case there is the real mail service and the mock service)