How to mock instance variable of class? - java

How do i mock the variables instantiated at class level..I want to mock GenUser,UserData. how do i do it...
I have following class
public class Source {
private GenUser v1 = new GenUser();
private UserData v2 = new UserData();
private DataAccess v3 = new DataAccess();
public String createUser(User u) {
return v1.persistUser(u).toString();
}
}
how do i mocked my v1 is like this
GenUser gu=Mockito.mock(GenUser.class);
PowerMockito.whenNew(GenUser.class).withNoArguments().thenReturn(gu);
what i have written for unit test and to mock is that
#Test
public void testCreateUser() {
Source scr = new Source();
//here i have mocked persistUser method
PowerMockito.when(v1.persistUser(Matchers.any(User.class))).thenReturn("value");
final String s = scr.createUser(new User());
Assert.assertEquals("value", s);
}
even if i have mocked persistUser method of GenUser v1 then also it did not return me "Value" as my return val.
thanks in adavanced.......:D

As in fge's comment:
All usages require #RunWith(PowerMockRunner.class) and #PrepareForTest annotated at class level.
Make sure you're using that test runner, and that you put #PrepareForTest(GenUser.class) on your test class.
(Source: https://code.google.com/p/powermock/wiki/MockitoUsage13)

Have a look at https://code.google.com/p/mockito/wiki/MockingObjectCreation - there are a couple of ideas there that may help you.

I don't know mockito, but if you don't mind using PowerMock and EasyMock, the following would work.
#Test
public void testCreateUser() {
try {
User u = new User();
String value = "value";
// setup the mock v1 for use
GenUser v1 = createMock(GenUser.class);
expect(v1.persistUser(u)).andReturn(value);
replay(v1);
Source src = new Source();
// Whitebox is a really handy part of PowerMock that allows you to
// to set private fields of a class.
Whitebox.setInternalState(src, "v1", v1);
assertEquals(value, src.createUser(u));
} catch (Exception e) {
// if for some reason, you get an exception, you want the test to fail
e.printStackTrack();
assertTrue(false);
}
}

Related

How to test update methods?

I am new in unit testing and use JUnit in my Java (Spring Boot) app. I sometimes need to test update methods, but when I search on the web, there is not a proper example or suggestion. So, could you please clarify me how to test the following update method? I think this may require a different approach than testing void. I also thought that while testing first mocking the record and then update its field and then update. Finally retrieve the record again and compare the updated properties. But I think there may be more proper approach than this inexperienced one.
public PriceDTO update(UUID priceUuid, PriceRequest request) {
Price price = priceRepository
.findByUuid(priceUuid)
.orElseThrow(() -> new EntityNotFoundException(PRICE));
mapRequestToEntity(request, price);
Price updated = priceRepository.saveAndFlush(price);
return new PriceDTO(updated);
}
private void mapRequestToEntity(PriceRequest request, Price entity) {
entity.setPriceAmount(request.getPriceAmount());
// set other props
}
You would need to do something along the following lines:
public class ServiceTest {
#Mock
private PriceRepository priceRepository;
(...)
#Test
public void shouldUpdatePrice() throws Exception {
// Arrange
UUID priceUuid = // build the Price UUID
PriceRequest priceUpdateRequest = // build the Price update request
Price originalPrice = // build the original Price
doReturn(originalPrice).when(this.priceRepository).findByUuid(isA(UUID.class));
doAnswer(AdditionalAnswers.returnsFirstArg()).when(this.priceRepository).saveAndFlush(isA(Price.class));
// Act
PriceDTO updatedPrice = this.service.update(priceUuid, priceUpdateRequest);
// Assert
// here you need to assert that updatedPrice is as you expect according to originalPrice and priceUpdateRequest
}
}
You need to mock the behavior of the class object priceRepository.
So you will have to write something like below to begin :
// priceRepository should be mocked in the test class
Mockito.when(priceRepository.findByUuid(any(UUID.class))).thenReturn(new Price());
So if your only intention is to verify if you called save, then something like this is probably what you are looking for:
#ExtendWith(MockitoExtension.class)
public class ServiceTest {
#Mock
private PriceRepository priceRepository;
#InjectMocks
private Service service;
#Test
public void update() throws Exception {
// Given
Price price = new Price();
price.setUid(UUID.randomUUID());
price.setPriceAmount(100);
when(priceRepository.findByUid(price.getUid()))
.thenReturn(price);
ArgumentCaptor<Price> priceArgument =
ArgumentCaptor.forClass(Price.class);
when(incidentRepository.saveAndFlush(priceArgument.capture()))
.thenAnswer(iom -> iom.getArgument(0));
// When
PriceRequest priceRequest = new PriceRequest();
priceRequest.setPriceAmount(123);
PriceDTO updatedPrice = this.service.update(price.getUid(), priceUpdateRequest);
// Then
assertThat(priceArgument.getValue().getPriceAmount())
.isEqualTo(123);
}
}

Is there any way to Mock private method call inside another method in Junit5

Below is the method which I wanted to test but as per my knowledge Junit5 doesn't support PowerMockito. So is there any way I can mock private method call inside another method ?
public Class MyClass {
private void sendEmailNotification(Checklist Checklist){
EmailService emailService = new EmailService();
BaseDTO esDO = newFolderService.getFolderByUri(ServicesUtils.getDecodedCaseNodeUriFromSelfLink(Checklist.getEs_uri()));
String esName = esDO.getName();
SharedInfo sharedInfo = Checklist.getShared_info();
sharedInfo.setEng_space_name(esName);
String reviewer = Checklist.getReviewer();
String ChecklistUri = Checklist.getUri();
String ChecklistName = Checklist.getName();
String targetPhase = Checklist.getTarget_phase();
String comment = Checklist.getComment();
String submitter = Checklist.getSubmitter();
String appURL = Checklist.getShared_info().getApp_url();
String ChecklistLink = buildChecklistURL(appURL, ChecklistUri);
String emailBodyTemplate;
String emailSubject;
emailBodyTemplate = EmailTemplates.getEmailTemplateByName(EmailConstants.TEMPLATE_DELIVERABLE_ACCEPTED_REJECTED_WITH_COMMENTS);
emailSubject = String.format(EmailConstants.ACCEPT_REJECT_WITH_COMMENTS_SUBJECT, ChecklistName, targetPhase);
emailBodyTemplate = EmailTemplates.replaceSharedVariable(emailBodyTemplate, sharedInfo);
emailBodyTemplate = EmailTemplates.replaceVariable(emailBodyTemplate, EmailConstants.VAR_TARGET_PHASE, targetPhase);
emailBodyTemplate = EmailTemplates.replaceVariable(emailBodyTemplate, EmailConstants.VAR_REVIEWER, reviewer);
emailBodyTemplate = EmailTemplates.replaceVariable(emailBodyTemplate, EmailConstants.VAR_CHECKLIST_ITEM_NAME, ChecklistName);
emailBodyTemplate = EmailTemplates.replaceVariable(emailBodyTemplate, EmailConstants.VAR_COMMENT, comment);
emailBodyTemplate = EmailTemplates.replaceVariable(emailBodyTemplate, EmailConstants.VAR_CHECKLIST_ITEM_URL, ChecklistLink);
try {
emailService.sendEmail(submitter, EmailConstants.EMAIL_SENDER, emailSubject, emailBodyTemplate);
} catch (RuntimeException e) {
Checklist.addError(messages.get(E_ACCEPT_REJECT_SEND_EMAIL));
}
}
//Method to be tested
public void method(Checklist checklist){
/*Some Code*/
sendEmail(checklist); /* want to ignore this, as throwing NullPointerException*/
/*Some Code*/
}}
I would suggest to change the scope of the private method to package or protected. You can then override it in a test class that extends your class.
I would not try to mock all the services used in the sendMail method, because your test will then depend on everything within the sendMail method even it doesn't need it. As a result your test must be changed whenever the internals of the sendMail method changes. This would be bad, because your test doesn't need the sendMail method - that's why you want to mock it.
Your test will also get very complex with all the mocks that are only necessary to make the sendMail method work.
A much better approach would be to extract the sendMail method into an interface and create an implementation with the content of the current sendMail method.
public interface ChecklistNotifier {
public void sendNotification(Checklist checklist);
}
public class EmailChecklistNotifier implements ChecklistNotifier {
public void sendNotification(Checklist checklist){
EmailService emailService = new EmailService();
BaseDTO esDO = newFolderService.getFolderByUri(ServicesUtils.getDecodedCaseNodeUriFromSelfLink(Checklist.getEs_uri()));
// ...
}
}
Your client class can then use a ChecklistNotifier.
public class ClientClass {
private ChecklistNotifier notifier;
public ClientClass(ChecklistNotifier notifier){
this.notifier = notifier;
}
public void method(Checklist checklist){
/*Some Code*/
notifier.sendnotification(checklist);
/*Some Code*/
}}
}
Now you can easily create a ClientClass instance in your test and pass it a ChecklistNotifier mock or just a simple implementation.
This approach also honors the SOLID principles, because you have
a single responsibility for both the ClientClass and the EmailChecklistNotifier
made it open-close - you can replace it with a mock or other implementions, maybe SMS notification
a segregated interface - ChecklistNotification
inversed the dependency and therfore decoupled the ClientClass from the mail sending implementation dependencies.
You are correct. Powermock does not yet support JUnit 5 and there is an open issue in their official github repository here.
There doesnt seem to be any easy way to mock private methods using Junit5 runner , unless of course you decide to use a custom classloader and do bytecode manipulation.
However, instead of mocking the whole method, I would suggest you to mock the dependency which is used to send the email (unless that dependency uses some final method).
And if you can't even do that, then best way is to use Junit4 instead of Junit5.

Mockito verify unit test - Wanted but not invoked. Actually, there were zero interactions with this mock

At first I want to sorry for my english.
I started to make some unit tests (i've never done this before, i'm a new guy in programming).
I have to test simple adding product to database (DynamoDB) method using mockito.verify but I have
"Wanted but not invoked. Actually, there were zero interactions with this mock."
Error and I don't know what to do.
This is my method code (in KitchenService class):
public Product addProduct(Product content) {
ObjectMapper objectMapper = new ObjectMapper();
String mediaJSON = null;
String authorJSON = null;
String productKindsJSON = null;
try {
mediaJSON = objectMapper.writeValueAsString(content.getMedia());
authorJSON = objectMapper.writeValueAsString(content.getAuthor());
productKindsJSON = objectMapper.writeValueAsString(content.getProductKinds());
} catch (JsonProcessingException e) {
logger.log(e.getMessage());
}
Item item = new Item()
.withPrimaryKey("id", UUID.randomUUID().toString())
.with("name", content.getName())
.with("calories", content.getCalories())
.with("fat", content.getFat())
.with("carbo", content.getCarbo())
.with("protein", content.getProtein())
.with("productKinds", productKindsJSON)
.with("author", authorJSON)
.with("media", mediaJSON)
.with("approved", content.getApproved());
Item save = databaseController.saveProduct(PRODUCT_TABLE, item);
logger.log(save + " created");
return content;
}
And this is test code:
#Test
public void addProduct() throws Exception {
KitchenService instance = mock(KitchenService.class);
Product expectedProduct = new Product();
expectedProduct.setName("kaszanka");
expectedProduct.setCalories(1000);
expectedProduct.setFat(40.00);
expectedProduct.setCarbo(20.00);
expectedProduct.setProtein(40.00);
expectedProduct.setProductKinds(Collections.singletonList(ProductKind.MEAT));
expectedProduct.setApproved(false);
Author expectedAuthor = new Author();
expectedAuthor.setId("testID");
expectedAuthor.setName("Endrju Golota");
expectedProduct.setAuthor(expectedAuthor);
Media expectedMedia = new Media();
expectedMedia.setMediaType(MediaType.IMAGE);
expectedMedia.setName("dupajasia");
expectedMedia.setUrl("http://blabla.pl");
expectedProduct.setMedia(expectedMedia);
verify(instance, times(1)).addProduct(expectedProduct);
}
This is what I got after test:
Wanted but not invoked:
kitchenService.addProduct(
model.kitchen.Product#a0136253
);
-> at service.kitchen.KitchenServiceTest.addProduct(KitchenServiceTest.java:80)
Actually, there were zero interactions with this mock.
Can someone tell me what im doing wrong?
What you should mock and verify is the databaseController dependency:
#Test
public void addProduct() throws Exception {
KitchenService instance = new KitchenService(); // you should create the class under test
DatabaseController controllerMock = mock(DatabaseController.class); // mock the controller
instance.setController(controller); // inject the mock
...
// Act
instance.addProduct(expectedProduct);
// Assert
verify(controller).saveProduct(Mockito.eq(PRODUCT_TABLE), Mockito.any(Item.class));
}
You should verify that the database is called within the service.. checking that it was invoked with any Item object should be enough.
Mocking is a tool that you only use for dependencies of the class that is being tested.
It appears that your test does not care about the Author, Media, and Product objects,
these are just dependencies of the method you want to test;
mock them.
Organization will greatly help your test;
do something like this:
public class TestKitchenService
{
private static String VALUE_PRODUCT_NAME = "VALUE_PRODUCT_NAME";
... use constants for other values as well. The value of the constant does not matter.
#InjectMocks
private KitchenService classToTest;
private InOrder inOrder;
#Mock
private Author mockAuthor;
#Mock
private DatabaseController mockDatabaseController;
#Mock
private Logger mockLogger;
#Mock
private Media mockMedia;
#Mock
private Product mockProduct;
#After
public void afterTest()
{
inOrder.verifyNoMoreInteractions();
verifyNoMoreInteractions(mockAuthor);
verifyNoMoreInteractions(mockDatabaseController);
verifyNoMoreInteractions(mockLogger);
verifyNoMoreInteractions(mockMedia);
verifyNoMoreInteractions(mockProduct);
}
#Before
public void beforeTest()
{
MockitoAnnotations.initMocks(this);
doReturn(mockAuthor).when(mockProduct).getAuthor();
doReturn(mockMedia).when(mockProduct).getMedia();
doReturn(VALUE_PRODUCT_NAME).when(mockProduct).getName();
doReturn(Collections.singletonList(ProductKind.MEAT)).when(mockProduct).getProductKinds();
... doReturns for the other product values.
inOrder = inOrder(
mockAuthor,
mockDatabaseController,
mockLogger,
mockMedia,
mockProduct);
ReflectionTestUtils.setField(
classToTest,
"databaseController",
mockDatabaseController);
ReflectionTestUtils.setField(
classToTest,
"logger",
mockLogger);
}
#Test
public void addProduct_success()
{
final Product actualResult;
actualResult = classToTest.addProduct(mockProduct);
assertEquals(
mockProduct,
actualResult);
inOrder.verify(mockProduct).getMedia();
inOrder.verify(mockProduct).getAuthor();
inOrder.verify(mockProduct).getProductKinds();
inOrder.verify(mockProduct).getName();
... inOrder.verify for the other product values.
inOrder.verify(mockDatabaseController).saveProduct(
eq(PRODUCT_TABLE),
any(Item.class));
}
}
The only things that should be mocked -- if anything -- are the ObjectMapper and databaseController. One only mocks collaborator objects, and almost never the system/class under test (very rare cases exist for "spying" on the SUT). And depending on what the ObjectMapper is and how transparent it's operation is, you may not really want to even mock that. Furthermore, as your implementation code is written instantiating the ObjectMapper by directly calling a constructor, you can't even mock it.
While I love the use of Mockito and mock objects, sometimes it is worthwhile to simply test with as many real objects as possible. This is especially true when your collaborators are simple, straightforward, have no side effects, and don't require complex initialization or setup. Only use mocks when it simplifies the test setup or verification.

JUnit test cases using mockito for Rest API

I am new to Mockito framework , I have a rest APi that connects my application to jasper server and do report related operations.I want to write junit test cases for rest API using mockito framework.
here i have class called Repositoryclient , Its constructor have instance of JasperServerInfo DAO class.
public class RepositoryClient {
public RepositoryClient(ServerInfo serverInfo) {
this.info = serverInfo;
try {
Session = Client.authenticate(info.username.trim(), info.password.trim());
}
catch (Exception e) {
}
}
public void Templates() { //this method brings list of present report in jasper server repository
try {
OperationResult<...> result = ....;
} catch (Exception e) {
INodeProcessor.logger.warn(e.toString());
throw Error.REPORT_TEMPLATE_LIST_ERROR.with();
}
}
So how to write JUnit test cases using mockito for this class please guide me through. Thank you in advance.
Well, the code could be improved to make it actually testable...
At the moment, there is no really good way to write unit tests for your code, since the constructor creates a JasperserverRestClient without any chance to change it. The least you can do is add another constructor (may be package access) to allow another JasperserverRestClient to be used. (Alternatively you could think about using a Factory pattern. but this might be to complicated.)
Then you could mock that...
JasperserverRestClient jasperServerClient = Mockito.mock( JasperserverRestClient.class );
RestClientSession session = Mockito.mock( RestClientSession.class );
Mockito.when( jasperServerClient.authenticate( "x", "y")).thenReturn( session );
RepositoryClient repositoryClient = new RepositoryClient(jasperServerClient);
This would at least allow you to test, that authenticate is called with the correct parameters via Mockito.verify.
Also it would allow you to test that the listTemplates method calls the session with the correct parameters (of course you'll need to so some more mocking there).
An additional constructor, assuming your tests are in the same package, would look like this:
RepositoryClient(JasperserverRestClient httpRestClient, JasperServerInfo serverInfo) {
this.info = serverInfo;
this.httpRestClient = httpRestClient;
try {
restClientSession = httpRestClient.authenticate(info.username.trim(), info.password.trim());
}
catch (Exception e) {
INodeProcessor.logger.warn(e.toString());
throw Error.REPOSITORY_CLIENT_ERROR.with();
}
}
This way you can inject a mocked instance of your JasperserverRestClient into your object.
A test of your listTemplates method would (addtionally) look like this...
X resourcesService = Mockito.mock( X.class ); // No clue what the resourcesService() method is supposed to return, fill that in here
Mockito.when ( restClientSession.resourcesService() ).thenReturn ( resourcesService );
...This will allow the part restClientSession.resourcesService() to work. Next...
Y resources = Mockito.mock( Y.class ); // Same thing here, don't know what resources() should return, insert that class here
Mockito.when( resourcesService.resources()).thenReturn ( resources );
This will allow the resources() call to work.
Next we do some trickery:
Mockito.when( resources.parameter( Mockito.anyString(), Mockito.anyString()).thenReturn(resources); // assuming that ResourceSearchParameter constant is a String
This will allow the parameter() calls to work by returning the same resources() object.
And so on... You will need to when(...).thenReturn(...) the search method to return a OperationResult<ClientResourceListWrapper>, etc. but that's the same stuff as above.
And in the end, we could verify that the methods were called with the right parameters...
Mockito.verify( resources, Mockito.times(1)).parameter(ResourceSearchParameter.FOLDER_URI, info.reportDirectory);
Mockito.verify( resources, Mockito.times(1)).parameter(ResourceSearchParameter.RECURSIVE, "false"
Mockito.verify( resources, Mockito.times(1)).search();
getting started example that i have :
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
#Test(priority = 31, groups = "success")
public void mockExample() {
Category category1 = mock(Category.class);
when(category1.getName()).thenReturn("Yess!!!");
System.out.println(category1.getName());
}
will print:"Yess!!!"
you can read from here :
http://examples.javacodegeeks.com/core-java/mockito/mockito-hello-world-example/
#RunWith(SpringJUnit4ClassRunner.class)
public class UserControllerTest {
#InjectMocks
private UserController userController;
#Mock
private RequestAttributes attrubutes;
#Mock
private UserService userService;
private MockMvc mockMvc;
#Before
public void setup() {
RequestContextHolder.setRequestAttributes(attrubutes);
this.mockMvc = MockMvcBuilders.standaloneSetup(userController).build();
}
#Test
public void getUserinfoDetails() {
String userId = "123";
String userName = "Test145";
List<UserDto> userDtoList = new ArrayList<>();
Mockito.when(userService.getAllUserInfo()).thenReturn(userDtoList);
Assert.assertNotNull(userController.getUserinfo());
Assert.assertNotNull(userDtoList);
Assert.assertNotNull(userId);
Assert.assertNotNull(userName);
}
#Test
public void getUserByIdDetails() {
String userId = "123";
UserDto userDto = new UserDto();
Mockito.when(userService.getUserByUserId(userId)).thenReturn(userDto);
Assert.assertNotNull(userController.getUserById(userId));
Assert.assertNotNull(userDto);
Assert.assertNotNull(userId);
}
}
===========================================================================
for reference use below link:(step by step explanation)
https://www.youtube.com/watch?v=yGLOexeJfKA&t=17s

Test class with a new() call in it with Mockito

I have a legacy class that contains a new() call to instantiate a LoginContext object:
public class TestedClass {
public LoginContext login(String user, String password) {
LoginContext lc = new LoginContext("login", callbackHandler);
}
}
I want to test this class using Mockito to mock the LoginContext as it requires that the JAAS security stuff be set up before instantiating, but I'm not sure how to do that without changing the login() method to externalize the LoginContext.
Is it possible using Mockito to mock the LoginContext class?
For the future I would recommend Eran Harel's answer (refactoring moving new to factory that can be mocked). But if you don't want to change the original source code, use very handy and unique feature: spies. From the documentation:
You can create spies of real objects. When you use the spy then the real methods are called (unless a method was stubbed).
Real spies should be used carefully and occasionally, for example when dealing with legacy code.
In your case you should write:
TestedClass tc = spy(new TestedClass());
LoginContext lcMock = mock(LoginContext.class);
when(tc.login(anyString(), anyString())).thenReturn(lcMock);
I am all for Eran Harel's solution and in cases where it isn't possible, Tomasz Nurkiewicz's suggestion for spying is excellent. However, it's worth noting that there are situations where neither would apply. E.g. if the login method was a bit "beefier":
public class TestedClass {
public LoginContext login(String user, String password) {
LoginContext lc = new LoginContext("login", callbackHandler);
lc.doThis();
lc.doThat();
return lc;
}
}
... and this was old code that could not be refactored to extract the initialization of a new LoginContext to its own method and apply one of the aforementioned solutions.
For completeness' sake, it's worth mentioning a third technique - using PowerMock to inject the mock object when the new operator is called. PowerMock isn't a silver bullet, though. It works by applying byte-code manipulation on the classes it mocks, which could be dodgy practice if the tested classes employ byte code manipulation or reflection and at least from my personal experience, has been known to introduce a performance hit to the test. Then again, if there are no other options, the only option must be the good option:
#RunWith(PowerMockRunner.class)
#PrepareForTest(TestedClass.class)
public class TestedClassTest {
#Test
public void testLogin() {
LoginContext lcMock = mock(LoginContext.class);
whenNew(LoginContext.class).withArguments(anyString(), anyString()).thenReturn(lcMock);
TestedClass tc = new TestedClass();
tc.login ("something", "something else");
// test the login's logic
}
}
EDIT:
Modern versions of Mockito provide similar functionality without needing the extra PowerMock library with the mockito-inline dependency (instead of the mockito-core dependency):
public class TestedClassTest {
#Test
public void testLogin() {
try (MockedConstruction<LoginContext> mockedConstruction =
Mockito.mockConstruction(LoginContext.class)) {
TestedClass tc = new TestedClass();
tc.login("something", "something else");
// test the login's logic
}
}
}
You can use a factory to create the login context. Then you can mock the factory and return whatever you want for your test.
public class TestedClass {
private final LoginContextFactory loginContextFactory;
public TestedClass(final LoginContextFactory loginContextFactory) {
this.loginContextFactory = loginContextFactory;
}
public LoginContext login(String user, String password) {
LoginContext lc = loginContextFactory.createLoginContext();
}
}
public interface LoginContextFactory {
public LoginContext createLoginContext();
}
public class TestedClass {
public LoginContext login(String user, String password) {
LoginContext lc = new LoginContext("login", callbackHandler);
lc.doThis();
lc.doThat();
}
}
-- Test Class:
#RunWith(PowerMockRunner.class)
#PrepareForTest(TestedClass.class)
public class TestedClassTest {
#Test
public void testLogin() {
LoginContext lcMock = mock(LoginContext.class);
whenNew(LoginContext.class).withArguments(anyString(), anyString()).thenReturn(lcMock);
//comment: this is giving mock object ( lcMock )
TestedClass tc = new TestedClass();
tc.login ("something", "something else"); /// testing this method.
// test the login's logic
}
}
When calling the actual method tc.login ("something", "something else"); from the testLogin() {
- This LoginContext lc is set to null and throwing NPE while calling lc.doThis();
Not that I know of, but what about doing something like this when you create an instance of TestedClass that you want to test:
TestedClass toTest = new TestedClass() {
public LoginContext login(String user, String password) {
//return mocked LoginContext
}
};
Another option would be to use Mockito to create an instance of TestedClass and let the mocked instance return a LoginContext.
In situations where the class under test can be modified and when it's desirable to avoid byte code manipulation, to keep things fast or to minimise third party dependencies, here is my take on the use of a factory to extract the new operation.
public class TestedClass {
interface PojoFactory { Pojo getNewPojo(); }
private final PojoFactory factory;
/** For use in production - nothing needs to change. */
public TestedClass() {
this.factory = new PojoFactory() {
#Override
public Pojo getNewPojo() {
return new Pojo();
}
};
}
/** For use in testing - provide a pojo factory. */
public TestedClass(PojoFactory factory) {
this.factory = factory;
}
public void doSomething() {
Pojo pojo = this.factory.getNewPojo();
anythingCouldHappen(pojo);
}
}
With this in place, your testing, asserts and verify calls on the Pojo object are easy:
public void testSomething() {
Pojo testPojo = new Pojo();
TestedClass target = new TestedClass(new TestedClass.PojoFactory() {
#Override
public Pojo getNewPojo() {
return testPojo;
}
});
target.doSomething();
assertThat(testPojo.isLifeStillBeautiful(), is(true));
}
The only downside to this approach potentially arises if TestClass has multiple constructors which you'd have to duplicate with the extra parameter.
For SOLID reasons you'd probably want to put the PojoFactory interface onto the Pojo class instead, and the production factory as well.
public class Pojo {
interface PojoFactory { Pojo getNewPojo(); }
public static final PojoFactory productionFactory =
new PojoFactory() {
#Override
public Pojo getNewPojo() {
return new Pojo();
}
};
I happened to be in a particular situation where my usecase resembled the one of Mureinik but I ended-up using the solution of Tomasz Nurkiewicz.
Here is how:
class TestedClass extends AARRGGHH {
public LoginContext login(String user, String password) {
LoginContext lc = new LoginContext("login", callbackHandler);
lc.doThis();
lc.doThat();
return lc;
}
}
Now, PowerMockRunner failed to initialize TestedClass because it extends AARRGGHH, which in turn does more contextual initialization... You see where this path was leading me: I would have needed to mock on several layers. Clearly a HUGE smell.
I found a nice hack with minimal refactoring of TestedClass: I created a small method
LoginContext initLoginContext(String login, CallbackHandler callbackHandler) {
new lc = new LoginContext(login, callbackHandler);
}
The scope of this method is necessarily package.
Then your test stub will look like:
LoginContext lcMock = mock(LoginContext.class)
TestedClass testClass = spy(new TestedClass(withAllNeededArgs))
doReturn(lcMock)
.when(testClass)
.initLoginContext("login", callbackHandler)
and the trick is done...

Categories

Resources