How to get intance of InvocationContext for unit tests - unit-testing

I am trying to write a unit test for a method taking the InvocationContext as parameter. More specifically here's the signature and essentials of the method.
#AroundInvoke
public Object autoLogMethodCall(final InvocationContext context) throws Exception {
String className = context.getClass().getSimpleName();
String packageName = context.getClass().getPackage().getName();
String methodName = context.getMethod().getName();
// Some logging stuff that is the target of actual testing
}
As you see, it is an interceptor method I intend to use for doing some basic logging for certain method calls.
Then I have unit test which I want to test that the logged messages will be properly formatted. But the problem is that that I can not create an instance of the InvocationContext to pass as a parameter for testing.
I have tried the following mocking.
#RunWith(PowerMockRunner.class)
public class AutoLoggingTest extends TestCase {
#Test
public void testAutoLogger() {
Logger log = new MyLogger(); // This is an implementation of org.apache.logging.log4j.Logger, which will hold the generated messages to check at the test
InvocationContext mockContext = PowerMockito.mock(InvocationContext.class);
Class clazz = AutoLoggingTest.class;
// The row causing the error 'MissingMethodInvocation'
PowerMockito.when(mockContext.getClass()).thenReturn(clazz);
try {
InterceptingClass ic = new InterceptingClass();
ic.setLogger(log);
ic.autoLogMethodCall(mockContext);
MyLogger myLogger = (MyLogger) ic.getLogger();
assertEquals(2, myLogger.getMessages().size());
} catch (Exception e) {
e.printStackTrace();
fail("Should not cause an exception in any case");
}
}
// Check the actual messages based on the information given in mocked InvocationContext object
}
But it does not work.
causes:
Tests in error:
AutoLoggingTest.testAutoLogger:25 ยป MissingMethodInvocation.
when() requires an argument which has to be 'a method call on a mock'.).
Any advice on how to do the mocking properly?

This required some thinking out of the box. Some mixed content with the mocked InvocationContext is needed. We can provide the testing class itself in the mocked InvocationContext object, thus I added and changed the following in the test class itself:
#RunWith(PowerMockRunner.class)
public class AutoLoggingTest extends TestCase {
// This method needs to be added here to provide it for mocked InvocationContext.
public void methodForLoggingTesting() {
}
#Test
public void testAutoLogger() {
Logger log = new MyLogger();
// Some renaming & refactoring after the initial stuff
AutoLoggingUtility alu = new AutoLoggingUtilityImplForTesting();
alu.setLogger(log);
InvocationContext mockContext = PowerMockito.mock(InvocationContext.class);
try {
Method testMethod = this.getClass().getMethod("methodForLoggingTesting");
PowerMockito.when(mockContext.getMethod()).thenReturn(testMethod);
PowerMockito.when(mockContext.proceed()).thenReturn(null);
} catch (Exception e) {
e.printStackTrace();
fail("Should not throw an exception, InvocationContext mocking failed!");
}
try {
alu.autoLogMethodCall(mockContext);
} catch (Exception e) {
e.printStackTrace();
fail("Should not throw an exception, logging failed!");
}
MyLogger myLogger = (MyLogger) alu.getLogger();
assertEquals(3, myLogger.getMessages().size());
// More tests to check the actual logged content
}
}
Also I realized I should provide the code for the 'MyLogger' as it wasn't quite trivial to implement for the test.
// Logger = org.apache.logging.log4j.Logger
// ExtendedLoggerWrapper = org.apache.logging.log4j.spi.ExtendedLoggerWrapper
#SuppressWarnings("serial")
protected class MyLogger extends ExtendedLoggerWrapper implements Logger {
private List<String> messages;
public MyLogger() {
super(null, null, null);
this.clearMessages();
}
// The actual log calls need to get stored to store the messages + prevent from NullPointerExceptions
#Override
public void trace(String msg) {
messages.add(msg);
}
// The actual log calls need to get stored to store the messages + prevent from NullPointerExceptions
#Override
public Object exit(Object obj) {
messages.add("Exited with: " + obj);
return obj;
}
public List<String> getMessages() {
return this.messages;
}
public void clearMessages() {
messages = new ArrayList<>();
}
/**
* You need to override all the method calls used to prevent NullPointerExceptions.
*
* #return <code>True</code> always, as required so in test.
*/
#Override
public boolean isTraceEnabled() {
return true;
}
}
And since there was some minor refactoring needed in the original Logging class, it now looks like this:
public abstract class AutoLoggingUtility {
private static final String logEntryTemplate = "Call to: %1$s#%2$s";
private static final String logExitTemplate = "'%1$s' call duration: %2$s ms";
public AutoLoggingUtility() {
}
#AroundInvoke
public Object autoLogMethodCall(final InvocationContext context) throws Exception {
// Note the methods Overridden in MyLogger
if (this.getLogger().isTraceEnabled()) {
String methodName = null;
String className = null;
try {
Method method = context.getMethod();
methodName = method.getName();
// Contains package
className = context.getMethod().getDeclaringClass().getName();
} catch (Exception e) {
// May not crash
methodName = "?method?";
className = "?class?";
}
Object[] args1 = { className, methodName };
String logMsg = String.format(getLogentrytemplate(), args1);
this.getLogger().trace(logMsg);
long startTime = System.currentTimeMillis();
try {
return this.getLogger().exit(context.proceed());
} finally {
Object[] args2 = { methodName, System.currentTimeMillis() - startTime };
logMsg = String.format(getLogexittemplate(), args2);
this.getLogger().trace(logMsg);
}
} else {
// mocked
return context.proceed();
}
/**
* Forces each extending class to provide their own logger.
*
* #return The logger of the extending class to direct the messages to correct logging context.
*/
abstract Logger getLogger();
}
The 'AutoLoggingUtilityImplForTesting' simply extends 'AutoLoggingUtility' to hold instance of MyLogger.
Summarum:
The trick is to provide instance of the test classes method 'methodForLoggingTesting' for the mocked object to return when the 'getMethod()' is called. => No need to try to mock excess stuff.

Related

Mockito Mock object being ignored and actual function being called

The mock object does not seem to work and the actual function is being called.
My Controller Class is as follows:-
#RestController
public class A {
#PostMapping(path = "/api/methods", consumes = "application/json", produces = "application/json")
public static ResponseEntity<Object> controllerFunction(#Valid #RequestBody String request,
#RequestHeader(value = "header-content") String header_content) {
JSONObject response = B.getAns(request);
return ResponseEntity.status(HttpStatus.OK).body(response.toString());
}
}
My Class B is as follows:-
#Service
public class B {
private static C client;
#Autowired
private C beanClient;
#PostConstruct
public void init() {
B.client = beanClient;
}
public static JSONObject getAns(String request) {
// This is the line that I intend to mock but gets ignored. It goes into the function search instead.
JSONObject resp = client.search(searchRequest, requestHeaderOptions); // assume that these two variables passed as arguments are not null and have some content.
// searchRequest is of type SearchRequest
// requestHeaderOptions is of type RequestOptions
return resp;
}
}
This is my test class :
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {
ControllerApplication.class, A.class, B.class, C.class
})
#ActiveProfiles("test")
public class ProjectTest {
#Mock
private C client;
#InjectMocks
A a;
private MockMvc mockMvc;
#BeforeSuite
public void setup() {
// I have tried both MockitoAnnotations.initMocks and openMocks. Both don't work
MockitoAnnotations.openMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(a).build();
}
#Test(enabled = true)
public void testing() throws Exception {
JSONObject obj = new JSONObject() // assume this object is not null
// This statement doesn't seem to work
doReturn(obj).when(client).search(any(SearchRequest.Class), any(RequestOptions.Class));
MockHttpServletRequestBuilder mockRequest = MockMvcRequestBuilders.post("/api/methods")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.header("header-content", "something")
.content("someData");
mockMvc.perform(mockRequest)
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().json(jsonResponse));
}
}
If you notice I have created a static variable of the class C in my Class B. This is part of the program structure itself and cannot be changed.
Is it possible to mock the client.search function given this code?
I was able to figure out what the problem was by running the test in debug mode.
I found that the #PostConstruct function in my Class B was getting called before my test function. So class B was creating its own beanClient object different from the mock in my test class. That's why it was going into the function and not mocking it.
I was able to resolve it by changing Class B like so:-
#Service
public class B{
#Autowired
private C client;
public JSONObject getAns(String request){
// This is the line that I intend to mock but gets ignored. It goes into the function search instead.
JSONObject resp =client.search(searchRequest,requestHeaderOptions); // assume that these two variables passed as arguments are not null and have some content.
// searchRequest is of type SearchRequest
// requestHeaderOptions is of type RequestOptions
return resp;
}
I had to change it into a non-static function.
A quick way to resolve your issue is to mock B and stub B.getAns directly.
In order to mock static B, you should add mockito-inline dependency to your pom.xml:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>3.8.0</version>
<scope>test</scope>
</dependency>
You can use Mockito.mockStatic in this way:
try (MockedStatic<B> mockedB = Mockito.mockStatic(B.class)) {
mockedB.when(() -> B.getAns(anyString())).thenReturn(obj);
// ...
}
Therefore, your test code will be:
#Test(enabled = true)
public void testing() throws Exception {
try (MockedStatic<B> mockedB = Mockito.mockStatic(B.class)) {
JSONObject obj = new JSONObject() // assume this object is not null
mockedB.when(() -> B.getAns(anyString())).thenReturn(obj);
MockHttpServletRequestBuilder mockRequest = MockMvcRequestBuilders.post("/api/methods")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.header("header-content", "something")
.content("someData");
mockMvc.perform(mockRequest)
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().json(jsonResponse));
}
}
Note that, when you use MockedStatic, you had better use try-with-resources pattern and do your test in this scope like above.
Read more about mockStatic: https://www.baeldung.com/mockito-mock-static-methods

Unit testing middleware with NUnit and NSubstitute

I've written a bit of middleware in an ASP.NET Core site and I'm trying to unit test it, mainly by following this guide that uses Moq.
My problem is finding an NUnit/NSubstitute equivalent for new DefaultHttpContext(). Substituting HttpContext will trigger the middleware, but it passes the try. I presume this is because of the issue quoted below. Does NUnit have a function to create a real HttpContext, or am I looking at a lot more infrastructure to achieve this?
I am sending an instance of DefaultHttpContext to the Invoke method. I can't use a mocked HttpContext in this scenario because the first middleware (the lambda function that we passed to the constructor) will need to write to the response. Hence the HttpResponse needs to be a real object not mocked.
Here is the code for my Test
[TestFixture]
public class ExceptionHelperTests
{
private IErrorRepository errorRepository;
private ExceptionHandler handler;
[SetUp]
public void Setup()
{
errorRepository = Substitute.For<IErrorRepository>();
}
[Test]
public async void Given_AnExceptionHappens_Then_ItShouldBeLogged()
{
// Arrange
const string username = "aUser";
var user = Substitute.For<ClaimsPrincipal>();
user.Identity.Name.Returns(username);
handler = new ExceptionHandler(
next: async (innerHttpContext) =>
{
innerHttpContext.User = user;
},
repository: errorRepository);
// Act
await handler.Invoke(new DefaultHttpContext());
// Assert
errorRepository.Received().LogException(Arg.Any<string>(), Arg.Any<Exception>(), Arg.Is(username));
}
}
Here is the IErrorRepository
public interface IErrorRepository
{
Exception LogException(string message, Exception ex, string userId);
void LogMessage(string message, string errorDetail, string userId);
}
And here is the middleware (with a simplified HandleException):
public sealed class ExceptionHandler
{
private readonly RequestDelegate _next;
private readonly IErrorRepository repository;
public ExceptionHandler(RequestDelegate next, IErrorRepository repository)
{
_next = next;
this.repository = repository;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
HandleException(ex, context.User.Identity.Name);
}
}
public void HandleException(Exception ex, string userId)
{
repository.LogException("An unhandled exception has occurred.", ex, userId);
}
}
DefaultHttpContext is just the default implementation of HttpContext abstract class.
You just could do
var HttpContextSub = Substitute.For<HttpContext>();

Mockito- thenReturn(true) still returns false on a mock object

I recently started reading up about Mockito. The following line of code must return true as per my understanding, but it returns false.
Test Class
public class PersonServiceImplTest {
Car car;
#InjectMocks
CarServiceImpl carService;
#Mock
CarDAOImpl carDAO;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testUpdateCar() {
int carId = 1;
Mockito.when(carDAO.getCarById(any(Integer.class))).thenReturn(new Car());
carService.updateCar(carId);
Mockito.when(carDAO.isLicenseExpired(any(Car.class))).thenReturn(true);
Mockito.verify(carDAO).updateCar(any(Car.class));
Mockito.verify(carDAO, times(1)).isLicenseExpired(any(Car.class));
Mockito.verify(carDAO, times(1)).issueLicense(any(Car.class));
}
}
Class to be tested
public class CarServiceImpl implements CarService {
#Autowired carDAO carDAO;
#Override
public Response updateCar(int carId) {
Car car =carDAO.getCarById(carId);
try {
carDAO.updateCar(car);
if(carDAO.isLicenseExpired(car)))
carDAO.issueLicense(car);
} catch (Exception e) {
log.error(e.getMessage());
return Response.status(Status.INTERNAL_SERVER_ERROR).build();
}
return Response.ok(Status.CREATED).build();
}
CarDAOImpl deals with the database, if needed, will update that too.
Thanks in advance.
These two lines are ordered incorrectly:
carService.updateCar(carId);
Mockito.when(carDAO.isLicenseExpired(Mockito.any(Car.class))).thenReturn(true);
The first line in that pair invokes the class-under-test and the second line sets up an expectation of how your carDAO should behave inside the class-under-test. So, you are setting the expectation after you invoke the class-under-test.
The following test will pass:
#Test
public void testUpdateCar() {
int carId = 1;
// establish expectations of how carDAO should behave inside updateCar()
Mockito.when(carDAO.getCarById(Mockito.any(Integer.class))).thenReturn(new Car());
Mockito.when(carDAO.isLicenseExpired(Mockito.any(Car.class))).thenReturn(true);
// invoke the class-under-test
carService.updateCar(carId);
// verify that CarService used CarDAO correctly
Mockito.verify(carDAO).updateCar(Mockito.any(Car.class));
Mockito.verify(carDAO).isLicenseExpired(Mockito.any(Car.class));
Mockito.verify(carDAO).issueLicense(Mockito.any(Car.class));
}

How to write a test for custom JUnit assertion?

Let's say I created my own assertSomething(...) method. How can I write a unit test to verify that it is correctly failing the test case using it?
If I understand you correctly I see the next way:
#Test
public void assertSomethingSuccessTest() {
// given
final Object givenActualResult = new Object(); // put your objects here
final Object givenExpectedResult = new Object(); // put your objects here
// when
assertSomething(givenActualResult, givenExpectedResult);
// then
// no exception is expected here
}
// TODO: specify exactly your exception here if any
#Test(expected = RuntimeException.class)
public void assertSomethingFailedTest() {
// given
final Object givenActualResult = new Object(); // put your objects here
final Object givenExpectedResult = new Object(); // put your objects here
// when
assertSomething(givenActualResult, givenExpectedResult);
// then
// an exception is expected here, see annotated expected exception.
}
If you need to verify an exception as well:
#Rule
public ExpectedException thrown = ExpectedException.none();
#Test
public void assertSomethingFailedTest() {
// given
final Object givenActualResult = new Object(); // put your objects here
final Object givenExpectedResult = new Object(); // put your objects here
// and
thrown.expect(RuntimeException.class);
thrown.expectMessage("happened?");
// when
assertSomething(givenActualResult, givenExpectedResult);
// then
// an exception is expected here, see configured ExpectedException rule.
}
You should take a look at the Rules, that were introduced in Junit 4.7. Especially TestWatcher.
TestWatcher is a base class for Rules that take note of the testing action, without modifying it. For example, this class will keep a log of each passing and failing test:
public static class WatchmanTest {
private static String watchedLog;
#Rule
public TestWatcher watchman= new TestWatcher() {
#Override
protected void failed(Throwable e, Description description) {
watchedLog+= description + "\n";
}
#Override
protected void succeeded(Description description) {
watchedLog+= description + " " + "success!\n";
}
};
#Test
public void fails() {
fail();
}
#Test
public void succeeds() {
}
}

PowerMockito Mocked final class gson.fromJson() Returns null

I have this code in main class -
try {
extraPlayer = gson.fromJson(jsonResponse, ExtraPlayer.class);// this returns null
} catch (Exception e) {
e.printStacktrace();
}
Here extraPlayer is coming as null
I have mocked #Mock Gson gsonMock;
Here ExtraPlayer is a static class.
I have written this test code -
#Test
public void test() {
String jsonResponse = "{\"status\":\"waiting\",\"no\":\"12\"}";
when(playerHandlerMock.resetPlayer("someString", "someString", "1",true
)).thenReturn(jsonResponse);
Gson gsonMock = PowerMockito.mock(Gson.class);
ExtraPlayer extraPlayer = new ExtraPlayer();
extraPlayer.setNo("12");
extraPlayer.setStatus("Waiting");
PowerMockito.mockStatic(ResetModemResponse.class); // using this for static class but didn't work.
PowerMockito.when(gsonMock.fromJson(jsonResponse, ExtraPlayer.class)).thenReturn(extraPlayer);
playerMock.performWaiting();
}
ExtraPlayer.java
public static class ExtraPlayer{
String no;
String status;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getNo() {
return code;
}
public void setNo(String no) {
this.no = no;
}
}
I have added these annotations to the test class -
#RunWith(PowerMockRunner.class)
#PrepareForTest(Gson.class)
why extraPlayer is null ? please help/Suggest.
If you were to use the standard #Mock and #InjectMocks annotation with mockito, then yes, the framework would take care of the injection of the mock into the class under test (regardless of the existence of the setters etc.).
Another thing is the playerMock, which i assume is the class under test.
Do not mock the class under test, create a normal instance and then inject the dependencies... the performWaiting method does not seem to accept the response String, so you would have to inject that also somehow (unless you left some parts out):
#Test
public void test() {
// Arrange
String jsonResponse = "{\"status\":\"waiting\",\"no\":\"12\"}";
Gson gsonMock = PowerMockito.mock(Gson.class);
ExtraPlayer extraPlayer = new ExtraPlayer();
extraPlayer.setNo("12");
extraPlayer.setStatus("Waiting");
PowerMockito.when(gsonMock.fromJson(jsonResponse, ExtraPlayer.class)).thenReturn(extraPlayer);
Player player = new Player();
player.setGson(gsonMock);
player.setResponse(jsonResponse);
// Act
player.performWaiting();
// Assert ...
}