I would like to write unit tests for a singleton class, but this class have dependencies to ui components. The class is PageManager and have some functionality to go back and go forward in the page history. With a unit test I like to test this history functionality but I don't want to initialize the ui stuff, becouse it is not needed for this test.
I'am new to JMockit and I tryed this out, but wihtout success:
Here is the original class to be mocked:
public final class PageManager {
private static final PageManager INSTANCE = new PageManager();
private final Set<Page> pages = new HashSet<>();
private Page currentPage;
private boolean initialized = false;
private PageManager() {
// Do some UI stuff
}
public static PageManager getInstance() {
return INSTANCE;
}
public void addPage(final Page page) {
pages.add(page);
}
public void initialize() {
// Do some UI stuff
initialized = true;
}
public Page getPage() { return currentPage; }
public void setPage(final Page page) { ... }
public void goBack() { ... };
public void goForward() { ... };
public boolean canGoBack() { ... };
public boolean canGoForward() { ... };
private void activatePage(final Page page) {
// Do some UI stuff
this.currentPage = page;
}
private void deactivatePage(final Page page) {
// Do some UI stuff
}
}
This is the mocked version:
public final class MockedPageManager extends MockUp<PageManager> {
PageManager instance;
#Mock
void $init(final Invocation invocation) {
instance = invocation.getInvokedInstance();
}
#Mock
void initialize() {
// Don't do UI stuff
Deencapsulation.setField(instance, "initialized", true);
}
#Mock
void activatePage(Page page) {
Deencapsulation.setField(instance, "currentPage", page);
page.activate();
}
#Mock
void deactivatePage(Page page) {
}
}
And a small test:
#Test
public void testGoBack() {
new MockedPageManager();
final Page p1 = new Page() { #Override public String getTitle() { return "p1"; } };
final Page p2 = new Page() { #Override public String getTitle() { return "p2"; } };
final PageManager pm = PageManager.getInstance();
pm.addPage(p1);
pm.addPage(p2);
pm.initialize();
pm.setPage(p1)
assertEquals(p1, pm.getCurrentPage());
pm.setPage(p2);
assertEquals(p2, pm.getCurrentPage())
assertTrue(pm.canGoBack());
pm.goBack();
assertEquals(p1, pm.getCurrentPage());
}
In this test, the $init method get invoked by JMockit correctly. The problem is, that an NullPointerExceptions is thrown in the test when pm.addPage(p1) is called. The stacktrace says, that the NPE occurs in the original class PageManager becouse the field Set pages is null.
My question is: Is this singleton class correctly mocked? Does the $init method override only the constructor or also the Java instance initializer i.e. Set pages = new HashSet<>();
As stated here, instance initializing blocks or statements are copied into each constructor (by the compiler). I suspect that JMockit uses reflection/byte code manipulation to mock the class's constructor, effectively circumventing all of the initializing code. Therefore the initializers are not executed and the set variable remains null. If you really have to make this work, try to initialize it properly in the mock. Better yet, refactor your class to allow its use in tests (e.g. an additional package private constructor for testing with injected dependencies; or move the page history functionality to its own class).
Related
I am testig a public method and I want to verify if a private method, that have mocked params, is called.
All the answers I have found are using invoke method, but this was removed since JMockit v1.36
public class ClassToTest{
public void methodToTest(){
DependencyClass abc = new DependencyClass();
if(privateMethod1()){
privateMethod2(abc);
}
}
private boolean privateMethod1(){ return true; }
private void privateMethod2(DependencyClass abc){ abc.doStuff(); }
}
public class testClassToTest{
#Mocked
DependencyClass abc;
#Tested
ClassToTest testedClass;
#BeforeEach
public void setUp() {
testedClass = new ClassToTest();
}
#Test
public void testMethod(){
new MockUp<ClassToTest>() {
#Mock
private boolean privateMethod1() {
return true;
}
};
testedClass.methodToTest();
new FullVerificationsInOrder() {{
abc = new DependencyClass();
//Check here if privateMethod2(abc) gets called once
}};
}
You can use Powermock to mock and verify private methods.
Please check https://github.com/powermock/powermock/wiki/MockPrivate
You have two ways:
To level up your method's scope from private to package-private and after it, your method will become visible in the test.
Refactoring your code and encapsulate the private method to Predicate and after it, you can test your primary method and Predicate separately.
You can't test the private method by Junit.
Is it possible to test code that is written in lambda function that is passed inside the method process?
#AllArgsConstructor
public class JsonController {
private final JsonElementProcessingService jsonElementProcessingService;
private final JsonObjectProcessingService jsonObjectProcessingService;
private final JsonArrayProcessingService jsonArrayProcessingService;
public void process(String rawJson) {
jsonElementProcessingService.process(json -> {
JsonElement element = new JsonParser().parse(json);
if (element.isJsonArray()) {
return jsonArrayProcessingService.process(element.getAsJsonArray());
} else {
return jsonObjectProcessingService.process(element.getAsJsonObject());
}
}, rawJson);
}
}
Since the lambda is lazy the function is not invoked (Function::apply) when I call JsonController::process so is there any way to check that jsonArrayProcessingService::process is called?
#RunWith(JMockit.class)
public class JsonControllerTest {
#Injectable
private JsonElementProcessingService jsonElementProcessingService;
#Injectable
private JsonObjectProcessingService jsonObjectProcessingService;
#Injectable
private JsonArrayProcessingService jsonArrayProcessingService;
#Tested
private JsonController jsonController;
#Test
public void test() {
jsonController.process("[{\"key\":1}]");
// how check here that jsonArrayProcessingService was invoked?
}
}
Just make it testable (and readable) by converting it to a method:
public void process(String rawJson) {
jsonElementProcessingService.process(this::parse, rawJson);
}
Object parse(String json) {
JsonElement element = new JsonParser().parse(json);
if (element.isJsonArray()) {
return jsonArrayProcessingService.process(element.getAsJsonArray());
} else {
return jsonObjectProcessingService.process(element.getAsJsonObject());
}
}
The relevant guiding principles I personally follow are:
anytime my lambdas require curly brackets, convert them to a method
organise code so that it can be unit tested
You may need to change the return type of the parse method to match whatever your processing services (which you didn’t show) return.
Given its relatively-basic redirection logic, don't you just want to confirm which of the #Injectables got called:
#Test
public void test() {
jsonController.process("[{\"key\":1}]");
new Verifications() {{
jsonArrayProcessingService.process(withInstanceOf(JsonArray.class));
}};
}
This question already has answers here:
Test class with a new() call in it with Mockito
(7 answers)
Closed 5 years ago.
This is not a duplicate of Test class with a new() call in it with Mockito. I'm trying to write a test to verify that certain methods are being called within the constructor of my spy object (mockToyFacade).
The class under test is ToyFactoryFacade. The idea is clients interact with the ToyFactoryFacade (which wraps a ToyFactory) to generate ToyFacades, which itself is a wrapper around the Toy object.
What I am trying to verify with Mockito?
I want to verify that addToyName(toyName) and addCreationTime(creationTimestamp) are being called on the ToyFacade. Both of these methods are called in the constructor of the ToyFacade.
What's the issue?
When I try to spy the ToyFacade, and verify that both aforementioned methods are called, I receive an error, which says "Actually, there were zero interactions with this mock." When I call the methods separately (i.e., not via the constructor), the verification check out correctly. I'm not sure what I'm doing incorrectly.
Test Code
public class ToyFactoryFacadeTest {
private Toy mockToy;
private ToyFacade mockToyFacade;
// System under test.
private ToyFactoryFacade toyFactoryFacade;
private ToyFactory mockToyFactory;
#Before
public void setup() {
mockToy = mock(Toy.class);
mockToyFacade = spy(new ToyFacade(mockToy, "Phone", System.currentTimeMillis()));
mockToyFactory = mock(ToyFactory.class);
toyFactoryFacade = new ToyFactoryFacade(mockToyFactory) {
#Override
public Toy getToyFacade(String toyName, long creationTimestamp){
return mockToyFacade;
}
};
}
#Test
public void testToyFactoryFacade() {
toyFactoryFacade.initializeAndGetToy("Phone", System.currentTimeMillis());
verify(mockToyFacade).addToyName("Phone");
verify(mockToyFacade).addCreationTime(anyLong());
}
}
Source Code
public class ToyFactoryFacade {
private final ToyFactory toyFactory;
public ToyFactoryFacade(ToyFactory toyFactory) {
this.toyFactory = toyFactory;
}
public ToyFacade initializeAndGetToy(String toyName, long creationTimestamp)
{
getToyFacade(toyName, creationTimestamp);
}
// For testing.
protected ToyFacade getToyFacade(String toyName, long creationTimestamp
{
return new ToyFacade(toyFactory.newToy(), toyName, creationTimestamp);
}
}
public class ToyFactory {
public Toy newToy() {
return new Toy();
}
}
public class ToyFacade {
private final Toy toy;
public ToyFacade(Toy toy, String toyName, long creationTimeStamp) {
this.toy = toy;
addToyName(toyName);
addCreationTime(creationTimestamp);
}
public void addToyName(String name) {
toy.addToyName(toyName);
}
public void addCreationTime(long timestamp) {
toy.addCreationTime(timestamp);
}
}
public class Toy {
public String toyName;
public String creationTimestamp;
public void addToyName(String name) {
toyName = name;
}
public void addCreationTime(long timestamp) {
creationTimestamp = timestamp;
}
}
Your test isn't doing what you expect because the method calls that you're trying to verify have already taken place before you create your spy. What you really want to do is to test the effect of those two method calls, rather than the calls themselves. This would look something like
verify(mockToy).addToyName("Phone");
verify(mockToy).addCreationTime(timestamp);
where timestamp is whatever you pass in in the setUp method.
I'm pretty new in unit testing and dont know how to test the following circunstances over a callback for this example class:
public class Foo {
private final ItemLoader loader;
private Bar bar;
public Foo(ItemLoader loader,Bar bar) {
super();
this.loader = loader;
this.bar=bar;
}
public void getItems(ItemStore.Callback callback) {
List<ItemData> itemData = bar.getItemData();
List<Item> items = this.loader.transform(itemData);
callback.onItemsLoaded(items);
}
}
That callback.onItemsLoaded is called with the result of loader.transform
My current test is:
public class ExampleTest extends BaseTestCase {
private Foo foo;
#Mock
private Bar mockBar;
#Mock
private ItemLoader mockItemLoader;
#Mock
private ItemStore.Callback itemLoadCallback;
public void setUp() {
MockitoAnnotations.initMocks(this);
foo = new Foo(mockItemLoader, mockBar);
}
public void testGetItems() {
List<ItemData> mockItemData = (List<ItemData>) mock(List.class);
when(mockBar.getItemData()).thenReturn(mockItemData);
foo.getItems(itemLoadCallback);
verify(mockItemLoader).transform(mockItemData);
}
}
It tests:
That loader.transform is called
That callback.onItemsLoaded is called
But I realised that if I change the last line of the Foo.getItems method like (Notice the null):
public void getItems(ItemStore.Callback callback) {
...
callback.onItemsLoaded(null);
}
The test keep pasing. So I'd need to test that callback.onItemsLoaded is called with the result of loader.transform
So I modified the test:
public void testGetItems() {
List<ItemData> mockItemData = (List<ItemData>) mock(List.class);
when(mockBar.getItemData()).thenReturn(mockItemData);
foo.getItems(itemLoadCallback);
verify(mockItemLoader).transform(mockItemData);
List<Item> resultItems = verify(mockItemLoader).transform(mockItemData);
verify(itemLoadCallback).onItemsLoaded(resultItems);
}
But it complains in the last line saying Argument(s) are different!
How can I fix the test
Because mockItemLoader is a mock, it will actually return an empty list from transform. If you want to make it return something different, you could set up an object for it to return. Basically, this will be your own List<Item>. So you can then stub the tranform method instead of verifying it; and use the same List<Item> when you verify the call to onItemsLoaded.
I have some anonymous Action classes in unit test code. Anonymous classes have no name. Class#getSimpleName returns "". It causes IndexOutOfBoundException when initialing convention-plugin.
PackageBasedActionConfigBuilder#buildConfiguration skips all interfaces, enums, annotations, and abstract classes. It should skip anonymous classes too. Add actionClass.isAnonymous() to the skip condition.
I have some anonymous Action classs in my unit test code. It's not good design.
private PageAction action;
#Before
public void beforeEach() {
action = new PageAction() {};
}
#Test
public void shouldAcceptAndPublicPageId() {
action.setPageId(1);
assertEquals(1, action.getRequestedPageId());
}
...
// To bypass complex logic in ViewPageAction
#Before
public void beforeEach() {
action = new ViewPageAction() {
boolean isPageBookmarkedByUser(Page page, User user) { return true; }
VisitPage visitPage() { return null; }
};
coreService = mockery.mock(CoreService.class);
action.setCoreService(coreService);
uiService = mockery.mock(UiService.class);
action.setUiService(uiService);
pageRepository = mockery.mock(PageRepository.class);
action.setPageRepository(pageRepository);
pageAttachmentRepository = mockery.mock(PageAttachmentRepository.class);
action.setPageAttachmentRepository(pageAttachmentRepository);
wiki = WikiTest.publicWiki();
action.setWiki(wiki);
User user = UserTest.FOO;
action.setUser(user);
}
#Test
public void success() {
final Page page = PageTest.FOO_PAGE;
final String text = "Content of the page";
final PageRevision latestRevision = MockPageRevision.FOO_REV2;
}
Thanks for any help
Sorry, this isn't a very clear question, so this is a guess:
Have you tried making your anonymous ViewPageAction into a proper class?
// somewhere in your test class...
private static class DummyViewPageAction extends ViewPageAction {
boolean isPageBookmarkedByUser(Page page, User user) { return true; }
VisitPage visitPage() { return null; }
}
// start of your #Before method...
#Before
public void beforeEach() {
action = new DummyViewPageAction();
...