JUnit: test that no error is logged - java

I am trying to test something like this:
try {
logger.info("message");
//do something
} catch(Exception e) {
logger.error(errorMessage);
}
I know that it's not a good practice to catch an Exception, but there is some legacy code and there is no time for refactoring.
So, I write an unit test so that a NullPointerException will be thrown inside try block, but now I don't know how to write the assert line(obviously, unit test have to fail all the time).
Please notice that I can`t use:
final Logger logger = LogManager.getLogger(AnaliticsService.class);
final Appender mockAppender = mock(Appender.class);
logger.addAppender(mockAppender);
final ArgumentCaptor<LoggingEvent> captor = ArgumentCaptor.forClass(LoggingEvent.class);
Log4jConfigHelper.getInstance().bufferConfiguration();
verify(mockAppender, times(x)).doAppend(captor.capture());
because I don`t know how many messages are logged when UT is running.

You should try to make a Mock for LoggerFactory.
First annotate your TestClass with:
#RunWith(PowerMockRunner.class)
#PrepareForTest({YourController.class, LoggerFactory.class})
Then make a test, which calls needed method and veryfies errors:
#Test
public void testErrorLogging() throws Exception {
mockStatic(LoggerFactory.class);
Logger logger = mock(Logger.class);
when(LoggerFactory.getLogger(any(Class.class))).thenReturn(logger);
YourController controller = new YourController();
controller.someMethod();
verify(logger).error(anyString());
}

Log messages are part of the user interface of your code. Code that does computations should not make assumptions about the manner in which log messages are made available to the user, the text and language of the log messages, or even whether messages are communicated as text (rather than, say, a graphical means). So computational code should delegate to an associated logger class (in the UI/presentation layer) that hides all those details.
If the computational code only requires that the associated logger conforms to an interface, and uses dependency injection for being associated with a logger class, it is easy to mock the logger to examine whether the computational code has requested logging.
So if the code to be tested is like this::
public class MyService
{
private final MyServiceLogger logger;
MyService(MyServiceLogger logger)
{
this.logger = Objects.requireNonNull(logger);
}
public void processFile(Path path) {
...
try{
...
} catch (EOFException e) {
logger.logUnexpectedEOF(path);
}
}
}
public interface MyServiceLogger
{
public logUnexpectedEOF(Path path);
}
public class MyServiceTextLogger implements MyServiceLogger
{
private final Logger textLogger = LogManager.getLogger(MyService.class);;
#Override
public logUnexpectedEOF(Path path) {
textLogger.error("unexpected EOF for file {}",path);
}
}
You can test it like this:
public class MyServiceTest
{
private static class MockMyServiceLogger implements MyServiceLogger
{
private Path path;
private int nCalls_logUnexpectedEOF;
#Override
public logUnexpectedEOF(Path path) {
++nCalls_logUnexpectedEOF;
this.path = path;
}
void assertCalled_logUnexpectedEOF(int nCalls, Path path) {
assertEquals("Called logUnexpectedEOF, nCalls", nCalls, nCalls_logUnexpectedEOF);
assertEquals("Called logUnexpectedEOF, path", path, this.path);
}
}
#Test
public void processFile_unexpectedEOF() {
Path testPath = ...
...
MockMyServiceLogger mockLogger = new MockMyServiceLogger();
MyService service = new MyService(mockLogger);
service.processFile(testPath);
mockLogger.assertCalled_logUnexpectedEOF(1, testPath);
}
#Test
public void processFile_OK() {
Path testPath = ...
...
MockMyServiceLogger mockLogger = new MockMyServiceLogger();
MyService service = new MyService(mockLogger);
service.processFile(testPath);
mockLogger.assertCalled_logUnexpectedEOF(0, null);
}
}

I write an unit test so that a NullPointerException will be thrown inside try block, but now I don't know how to write the assert line(obviously, unit test have to fail all the time).
You don't need to check for an exception this way. A test which throws an Exception fails.
} catch(Exception e) {
logger.error(errorMessage, e);
throw e; // report the error to the test
}
Note: when to throw an error to the testing framework it will log/print it so I suspect you don't need to be catching it in the first place.

Related

Spring boot -> why i dont need to catch my exceptions inside a service?

im bulding a rest api with spring boot and i cant figure out why i dont have to catch my own exceptions inside a method..
please take a look at the FIRST_IMPORTANT_LINE at the code, this function (isemptyfile) throws an EmptyFileException at SECOND_IMPORTANT_LINE.
it turns out that intellij doesnt make me to catch this exception in THIRD_IMPORTANT_LINE.
for some reason my exception just get to somewhere else,
i.e. when i debug the test of ImageService with junit the exception goes right to the test method after reaching SECOND_IMPORTANT_LINE
someone can explain to me this behavior? and how can i handle this exception properly?
thanks!
here is the code ->
#Service
public class ImageService {
private final FileStore fileStore;
private final UserService userService;
#Autowired
public ImageService(FileStore fileStore, UserService userService) {
this.fileStore = fileStore;
this.userService = userService;
}
public void uploadImageToS3(int userProfileId, MultipartFile file){
try{
isFileEmpty(file); //***********FIRST_IMPORTANT_LINE***********
isImage(file);
User user = userService.getUser(userProfileId);
Map<String,String> metadata = extractMetaData(file);
String path = MessageFormat.format("{0}/{1}", BucketName.PROFILE_IMAGE.getBucketName(),user.getId());
String[] filenameArray = file.getOriginalFilename().split("\\.");
String fileNameWithoutExtension = filenameArray[0];
String extension = filenameArray[1];
String fileName = MessageFormat.format("{0}-{1}.{2}",fileNameWithoutExtension,UUID.randomUUID(),extension);
fileStore.save(path,fileName,Optional.of(metadata),file.getInputStream());
//update user link -- filename
}
catch (IOException|UserNotFoundException e){
///***********THIRD_IMPORTANT_LINE***********
throw new IllegalStateException(e);
}
}
private void isFileEmpty(MultipartFile file){
if(file == null || file.isEmpty()){
throw new EmptyFileException("cannot upload empty file"); //***********SECOND_IMPORTANT_LINE***********
}
}
}
IllegalStateException inherits from RuntimeException, which is an unchecked exception. See JavaDocs for that one, specifically this line:
RuntimeException and its subclasses are unchecked exceptions. Unchecked exceptions do not need to be declared in a method or constructor's throws clause if they can be thrown by the execution of the method or constructor and propagate outside the method or constructor boundary.

Microprofile #Fallback not working in native image mode

Having the following code running on Quarkus:
#Singleton
#RegisterForReflection
public class StoreService {
private static final Logger LOGGER = Logger.getLogger(StoreService.class);
#Inject
#RestClient
StoresApiClient client;
#CacheResult(cacheName = "stores")
#Fallback(fallbackMethod = "allFallbackStores")
public List<Store> allStores() {
// call REST API using client
}
#SuppressWarnings("unused")
public List<Store> allFallbackStores() {
try {
LOGGER.info("Falling back to internal stores list");
...
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
the fallback mechanism is working properly in regular JDK mode. On the other hand in native image mode, #Fallback annotation is not being respected and an exception is thrown after unsuccessful API call. What might be a reason for that if #RegisterForReflection annotation is in place?

Not able to unit test the exception thrown in this method using mockito

I have written the following unit test using mockito to test my EmailService.java class. What I am not sure is if I am testing this correctly or not (both happy path and exception scenario).
Moreover I am getting this Error: 'void' type not allowed here
in the following code snippet in my unit test
when(mockEmailService.notify(anyString())).thenThrow(MailException.class);
I understand that since my notify() method is returning void I am getting that exception. But not sure how to resolve this. Is there any code change required in my unit test or actual class or both?
Can somebody please guide.
EmailServiceTest.java
public class EmailServiceTest {
#Rule
public MockitoJUnitRule rule = new MockitoJUnitRule(this);
#Mock
private MailSender mailSender;
#Mock
private EmailService mockEmailService;
private String emailRecipientAddress = "recipient#abc.com";
private String emailSenderAddress = "sender#abc.com";
private String messageBody = "Hello Message Body!!!";
#Test
public void testNotify() {
EmailService emailService = new EmailService(mailSender, emailRecipientAddress, emailSenderAddress);
emailService.notify(messageBody);
}
#Test(expected = MailException.class)
public void testNotifyMailException() {
when(mockEmailService.notify(anyString())).thenThrow(MailException.class);
EmailService emailService = new EmailService(mailSender, emailRecipientAddress, emailSenderAddress);
emailService.notify(messageBody);
}
}
EmailService.java
public class EmailService {
private static final Log LOG = LogFactory.getLog(EmailService.class);
private static final String EMAIL_SUBJECT = ":: Risk Assessment Job Summary Results::";
private final MailSender mailSender;
private final String emailRecipientAddress;
private final String emailSenderAddress;
public EmailService(MailSender mailSender, String emailRecipientAddress,
String emailSenderAddress) {
this.mailSender = mailSender;
this.emailRecipientAddress = emailRecipientAddress;
this.emailSenderAddress = emailSenderAddress;
}
public void notify(String messageBody) {
SimpleMailMessage message = new SimpleMailMessage();
message.setSubject(EMAIL_SUBJECT);
message.setTo(emailRecipientAddress);
message.setFrom(emailSenderAddress);
message.setText(messageBody);
try {
mailSender.send(message);
} catch (MailException e) {
LOG.error("Error while sending notification email: ", e);
}
}
}
I'm going to assume that the EmailService implementation here is correct and focus on the tests. They're both flawed. While the testNotify executes without errors, it isn't actually testing anything. Technically, it will at least confirm that notify does not throw an exception when mailService does not throw an exception. We can do better.
The key to writing good tests is to ask yourself, "What is this method supposed to do?" You should be able to answer that question before you even write the method. For specific tests, ask "What should it do with this input?" or "What should it do when its dependency does this?"
In the first case, you create a MailService passing it a MailSender and the to and from addresses. What is that MailService instance supposed to do when its notify method is called? It's supposed pass a SimpleMailMessage to MailSender through the send method. Here's how you do that (Note, I assumed that MailSender actually takes a MailMessage interface instead of SimpleMailMessage):
#Mock
private MailSender mailSender;
private EmailService emailService;
private String emailRecipientAddress = "recipient#abc.com";
private String emailSenderAddress = "sender#abc.com";
private String messageBody = "Hello Message Body!!!";
#Before
public void setUp(){
MockitoAnnotations.initMocks(this);
emailService = new EmailService(mailSender, emailRecipientAddress, emailSenderAddress);
}
#Test
public void testMessageSent() throws MailException {
ArgumentCaptor<MailMessage> argument = ArgumentCaptor.forClass(MailMessage.class);
emailService.notify(messageBody);
Mockito.verify(mailSender).send(argument.capture());
Assert.assertEquals(emailRecipientAddress, argument.getValue().getTo());
Assert.assertEquals(emailSenderAddress, argument.getValue().getFrom());
Assert.assertEquals(messageBody, argument.getValue().getText());
}
This makes sure that EmailService actually sends the message you'd expect based on arguments passed to its constructor and notify method. We do not care whether or not MailSender does its job correctly in this test. We just assume it works- presumably because it's either tested elsewhere or part of a provided library.
The test for the exception is a little more subtle. Since the exception is caught, logged, and then ignored there's not as much to test. I personally don't bother checking that anything is logged. What we really want to do is confirm that if MailSender throws MailException then notify will not throw an exception. If MailException is a RuntimeException then we should test this. Basically, you just mock mailSender to throw an exception. If EmailService does not handle it correctly, then it will throw an exception and the test will fail (This uses the same setup as the previous example):
#Test
public void testMailException() throws MailException {
Mockito.doThrow(Mockito.mock(MailException.class)).when(mailSender).send(Mockito.any(MailMessage.class));
emailService.notify(messageBody);
}
Alternatively, we can catch the MailException and then explicitly fail the test:
#Test
public void testMailExceptionAlternate() {
try {
Mockito.doThrow(Mockito.mock(MailException.class)).when(mailSender).send(Mockito.any(MailMessage.class));
emailService.notify(messageBody);
} catch (MailException ex){
Assert.fail("MailException was supposed to be caught.");
}
}
Both approaches confirm the desired behavior. The second is more clear in what it's testing. The downside, though, is that if notify were allowed to throw a MailException in other circumstances, then that test might not work.
Finally, if MailException is a checked exception- that is it not a RuntimeException- then you would not even need to test this. If notify could possibly throw a MailException then the compiler would require it declare it in the method signature.
You shouldn't really be mocking a class that you are trying to test. I think what you really want to do here is to mock the MailSender to throw an exception. I notice you're already using a mock MailSender in your successful test. Just use this again and set the expectation:
when(mailSender.send(any(SimpleMailMessage.class))).thenThrow(MailException.class);
But as mentioned in #GhostCat's answer you are doing exception handling in the method so the you'd need to specify a different expectation other than the exception to be thrown. You could mock the logger but mocking static loggers is usually a lot more effort that it's worth. You might want to consider reworking your exception handling to make it easier to test.
Unfortunately there are simply many things wrong with the code given in the question. For example: you instruct your test to throw an exception. And to expect that exception to make it up into the test.
But:
try {
mailSender.send(message);
} catch (MailException e) {
LOG.error("Error while sending notification email: ", e);
}
Your production code is catching the exception. So you wrote a test that can only pass when your production code is incorrect!
Now, if the test is wrong: you could look into mocking that logger object; to verify that a call to it happens. And you would change the test case to not expect any exception. That's the whole point of try/catch; isn't it.
Or the other way round: if not catching is what you want; your unit test told you that the try/catch has to go away.
As said, that is just one problem here - the other answers do a good job listing them.
From that point of view may answer is: don't try to learn unit testing by trial and error. Instead: get a good book or tutorial and learn how to do unit testing - including how to use mocking frameworks correctly.
See this doc on how to mock void methods with exception:
In your case it should be something like this:
doThrow(new MailException()).when(mockEmailService).notify( anyString() );
Your testNotify does not do proper testing. After calling actual notify method is does not check the expected outcome.
Your testNotifyMailException first mocks notify and then calls actual notify method on non mocked EmailService. The whole point of mocking notify is to test the code that calls it and not the actual method you are mocking.
Based on the above-mentioned responses I got it working by modifying both my actual class and unit test.
EmailSendException.java (New class added in order to promote testablity)
public class EmailSendException extends RuntimeException {
private static final long serialVersionUID = 1L;
public EmailSendException(String message) {
super(message);
}
public EmailSendException(String message, Throwable cause) {
super(message, cause);
}
}
EmailService.java (Instead of catching, throwing a RuntimeException)
public class EmailService {
private static final Log LOG = LogFactory.getLog(EmailService.class);
private static final String EMAIL_SUBJECT = ":: Risk Assessment Job Summary Results::";
private final MailSender mailSender;
private final String emailRecipientAddress;
private final String emailSenderAddress;
private static final String ERROR_MSG = "Error while sending notification email";
public EmailService(MailSender mailSender, String emailRecipientAddress,
String emailSenderAddress) {
this.mailSender = mailSender;
this.emailRecipientAddress = emailRecipientAddress;
this.emailSenderAddress = emailSenderAddress;
}
public void notify(String messageBody) {
SimpleMailMessage message = new SimpleMailMessage();
message.setSubject(EMAIL_SUBJECT);
message.setTo(emailRecipientAddress);
message.setFrom(emailSenderAddress);
message.setText(messageBody);
try {
mailSender.send(message);
} catch (MailException e) {
throw new EmailSendException(ERROR_MSG, e);
}
}
}
EmailServiceTest.java (Mocking and testing )
public class EmailServiceTest {
#Rule
public MockitoJUnitRule rule = new MockitoJUnitRule(this);
#Mock
private MailSender mailSender;
private String emailRecipientAddress = "recipient#abc.com";
private String emailSenderAddress = "sender#abc.com";
private String messageBody = "Hello Message Body!!!";
#Mock
private EmailService mockEmailService;
#Test
public void testNotify() {
EmailService EmailService = new EmailService(mailSender, emailRecipientAddress, emailSenderAddress);
EmailService.notify(messageBody);
}
#Test(expected = KlarnaEmailSendException.class)
public void testNotifyMailException() {
doThrow(new KlarnaEmailSendException("Some error message")).when(mockKlarnaEmailService).notify(anyString());
mockKlarnaEmailService.notify(messageBody);
}
}

mocking nested function is giving NPE

Hi I am getting Null Pointer Exception while trying to write unit test cases
Here is the class detail:
public CreateDraftCampaignResponse createDraftCampaign(CreateDraftCampaignRequest request) throws InvalidInputsException,
DependencyException, UnauthorizedException {
CreateDraftCampaignResponse draftCampaignResponse = null;
try {
DraftCampaignDetails createdDraft = draftCampaignI.createDraftCampaign(ConvertionUtil
.getDraftCampaignDetailsfromCreateDraftRequest(request));
draftCampaignResponse = new CreateDraftCampaignResponse();
draftCampaignResponse.setDraftCampaignId(createdDraft.getDraftId());
}
catch (Exception e) {
log.error("Create Draft Campaign Exception", e);
throw e;
}
return draftCampaignResponse;
}
This is the ConvertionUtil class:
public static DraftCampaignDetails getDraftCampaignDetailsfromCreateDraftRequest(CreateDraftCampaignRequest request) {
DraftCampaignDetails draftCampaign = new DraftCampaignDetails();
DraftCampaignDetailsBase draftCampaignDetailsBase = request
.getDraftCampaignDetailsBase(); (This is giving exception)
draftCampaign.setCampaignBudget(draftCampaignDetailsBase
.getCampaignBudget());
draftCampaign.setCampaignName(draftCampaignDetailsBase
.getCampaignName());
draftCampaign.setDraftCampaignState(draftCampaignDetailsBase
.getDraftCampaignState());
draftCampaign.setCreatedUser(request.getUser());
draftCampaign.setObfuscatedEntityId(request.getObfuscatedEntityId());
draftCampaign.setCampaignInfo(request.getCampaignInfo());
return draftCampaign;
}
Thsi is what I tried:
#Test
public void createDraft_newDraft() {
DraftCampaignActivity draftContoller = new DraftCampaignActivity();
CreateDraftCampaignRequest request = createRequest();
DraftCampaignDetails details = buildDraftDetails();
if(draftCampaignI == null){
System.out.println("sccdscscd");
}
//ConvertionUtil action1 = PowerMockito.mock(ConvertionUtil.class);
//PowerMockito.when(action1.getDraftCampaignDetailsfromCreateDraftRequest(request)).thenReturn(details);
when(util.getDraftCampaignDetailsfromCreateDraftRequest(request)).thenReturn(details);
when(draftCampaignI.createDraftCampaign(details)).thenReturn(details);
CreateDraftCampaignResponse response = new CreateDraftCampaignResponse();
draftContoller.createDraftCampaign(request);
response.setDraftCampaignId(details.getDraftId());
Assert.assertEquals(response.getDraftCampaignId(),"ww");
}
I am getting NPE. I am a novice in Mockito and other framework. Please help!
It doesn't work because you try to mock a static method and you don't do it properly such that it calls the real method which leads to this NPE in your case.
To mock a static method using Powermock, you need to:
Use the #RunWith(PowerMockRunner.class) annotation at the class-level of the test case.
Use the #PrepareForTest(ClassThatContainsStaticMethod.class) annotation at the class-level of the test case.
Use PowerMock.mockStatic(ClassThatContainsStaticMethod.class) to mock all methods of this class.
So in your case, you should have something like:
#RunWith(PowerMockRunner.class)
public class MyTestClass {
#Test
#PrepareForTest(ConvertionUtil.class)
public void createDraft_newDraft() {
...
PowerMockito.mockStatic(ConvertionUtil.class);
PowerMockito.when(
ConvertionUtil.getDraftCampaignDetailsfromCreateDraftRequest(request)
).thenReturn(details);
...
}
More details about How to mock a static method with Powermock.

#BeforeClass annotations invoking methods twice when using Arquillian on remote server

We're transitioning from using TestNG with an embedded JBoss to using Arquillian with a remote server.
We are running a simple test that has a method annotated with #BeforeClass that does some test setup. After a lot of digging, it looks like that setup method is being called twice: once on the console where we're executing our Maven command to run the test and again when the test war is deployed to our remote server and the test runs. These are two separate JVMS - one running outside the container and another running inside the container. My preference is to just have the latter run.
Is this the behavior I should expect or is there something I may be missing?
For now, we're actually checking to see if we're in the container or not and, if so, we run our setup code. This works but I'd like to know if there's a better way.
Some snippets of our code (please ignore the simplicity of the code and the fact that the setupComponents method really isn't needed here, there are much more complicated tests that we're migrating that will need this functionality):
public class BaseTest extends Arquillian
{
private static Log log = LogFactory.getLog( SeamTest.class );
#Deployment
public static Archive<?> createDeployment()
{
// snip... basically, we create a test war here
}
/**
* todo - there might be a better way to do this
*/
private boolean runningInContainer()
{
try
{
new InitialContext( ).lookup( "java:comp/env" );
return true;
}
catch (NamingException ex)
{
return false;
}
}
#BeforeClass
public void setupOnce() throws Exception
{
getLog().debug( "in setupOnce(): " + runningInContainer() );
if ( runningInContainer() )
{
new ComponentTest()
{
protected void testComponents() throws Exception
{
setupComponents();
}
}.run();
}
}
public User createUser()
{
// ...
}
public Log getLog()
{
// snip...
}
public UserDao getUserDao()
{
// ...
}
public abstract class ComponentTest
{
protected abstract void testComponents() throws Exception;
public void run() throws Exception
{
try {
testComponents();
} finally {
}
}
}
}
public class UserDaoTest extends BaseTest
{
UserDao userDao;
#Override
protected void setupComponents()
{
getLog().debug( "in setupComponents: " + runningInContainer() );
userDao = getUserDao();
}
#Test
public void testGetUser() throws Exception
{
getLog().debug( "in testGetUser: " + runningInContainer() );
new ComponentTest()
{
protected void testComponents() throws Exception
{
User user0 = createUser();
user0.setName( "frank" );
userDao.merge( user0 );
User retrievedUser = userDao.findByName( "frank" );
assertNotNull( retrievedUser );
}
}.run();
}
}
This basically gives me output that looks like this:
From the console where mvn is being executed:
in setupOnce(): false
From the jboss server:
in setupOnce(): true
in setupComponents: true
in testGetUser: true
This is "expected" behaviour. Not really nice, but it is how Arqullian works.
JUnit
#BeforeClass / #AfterClass are only executed on ClientSide
The state of the test class is lost between #Test, in container the whole lifecycle is repeated for each test
#Before / #After are executed depending on the runmode (client/server)
TestNG
everything is run both on server and client

Categories