I've been working with JMockit and its admittedly steep learning curve. I'm pretty new with mocking in general so please excuse the ignorance.
I'm trying to mock out the Logger interface so that I can verify the catch statement is working correctly. Call this an exercise in understanding how JMockit works. The implementing class for the Logger interface is Log4jLoggerAdapter so I thought if I passed an instance of that into my Expectations() block, JMockit would use dynamic partial mocking and "see" my logger statement. Instead, I get the following error:
mockit.internal.MissingInvocation: Missing invocation of: org.slf4j.impl.Log4jLoggerAdapter#error(String msg, Throwable t)
The Class Being Tested
public class MyLoggedClass {
private static final Logger LOGGER = LoggerFactory.getLogger(MyLoggedClass.class);
... // Other variables
#Override
public void connect() {
String info = getServiceInfo();
try {
connector = MyConnectionFactory.connect(info);
} catch (Exception e) {
LOGGER.error("Exception connecting to your service with: " + info, e);
}
}
... // Other methods
}
My #Test
public class MyLoggedClassTest {
#Tested
MyLoggedClass myLoggedClass;
#Test
public void myLoggingTest(#Mocked final Log4jLoggerAdapter logger){
new Expectations(MyConnectionFactory.class, logger){{
MyConnectionFactory.connect(anyString);
result = new Exception();
logger.error(anyString, (Throwable)any);
}};
myLoggedClass.connect();
}
I'd detail the other approaches I've tried but this page would turn into a book. This is my best approach. Any ideas?
* Update * (yes, that was quick)
I changed #Mocked to #Cascading and removed the logger field from my Expectations signature and it worked. I don't understand why. Can someone please provide an explanation? Fumbling about until you stumble on something that works but you don't understand is not a recipe for success. See below:
#Test
public void myLoggingTest(#Cascading final Log4jLoggerAdapter logger){
new Expectations(MyConnectionFactory.class){{
MyConnectionFactory.connect(anyString);
result = new Exception();
logger.error(anyString, (Throwable)any);
}};
myLoggedClass.connect();
}
No need for partial mocking in this case, just mock MyConnectionFactory in the usual way. The only tricky part is how to mock the class that implements the Logger interface, considering that it's instantiated from a static class initializer. As it happens, there is a feature in the mocking API for that (using JMockit 1.14):
public class MyLoggedClassTest
{
#Tested MyLoggedClass myLoggedClass;
#Test
public void myLoggingTest(
#Mocked MyConnectionFactory conFac,
#Capturing final Logger logger)
{
new Expectations() {{
MyConnectionFactory.connect(anyString);
result = new Exception();
}};
myLoggedClass.connect();
new Verifications() {{
logger.error(anyString, (Throwable)any);
}};
}
}
With a #Capturing mocked type, any implementation class will get mocked, so the test doesn't need to know about Log4jLoggerAdapter.
Related
adding test cases for getStudent method, this is having internal calls
1- is repository call - stubbing this call working fine
2- validate user call - stubbing this call not working, showing some error and test case failed.
Service Class
#Service
public class StudentServiceImpl implements StudentService {
#Autowired
FakeStudentRepository fakeStudentRepository;
#Override
public Optional<Student> getStudent(int id) {
Optional<Student> student = fakeStudentRepository.getStudent(id);
boolean isValid = myClass().isValidUser(student.get().getId());
if(!isValid) {
return Optional.empty();
}
return student;
}
public MyTestClass myClass() {
return new MyTestClass();
}
}
MyTestClass
public class MyTestClass {
public boolean isValidUser(int id) {
return true;
}
}
Test Class
#SpringBootTest
class StudentServiceImplTest {
#Mock
FakeStudentRepository fakeStudentRepository;
#InjectMocks
StudentServiceImpl studentServiceImpl;
#BeforeEach
public void setup() {
studentServiceImpl = Mockito.spy(StudentServiceImpl.class);
MockitoAnnotations.initMocks(this);
}
#Test
void getStudent() {
Optional<Student> student = Optional.of(Student.builder().id(1).firstName("Rahul").lastName("rahul")
.mobile("XXXXXX").build());
Mockito.doReturn(student)
.when(fakeStudentRepository).getStudent(ArgumentMatchers.anyInt());
Mockito.doReturn(false)
.when(studentServiceImpl).myClass().isValidUser(ArgumentMatchers.anyInt());
Optional<Student> resultStudent = studentServiceImpl.getStudent(student.get().getId());
assertEquals(resultStudent.get().getId(), student.get().getId());
}
}
Error
org.mockito.exceptions.misusing.WrongTypeOfReturnValue: Boolean
cannot be returned by myClass() myClass() should return MyTestClass
If you're unsure why you're getting above error read on. Due to the
nature of the syntax above problem might occur because:
1. This exception might occur in wrongly written multi-threaded tests. Please refer to Mockito FAQ on limitations of concurrency
testing.
2. A spy is stubbed using when(spy.foo()).then() syntax. It is safer to stub spies -
- with doReturn|Throw() family of methods. More in javadocs for Mockito.spy() method.
The error Message says it: You are mocking studentServiceImpl.myClass() and try to return true. It’s not possible to mock the end of a call chain as you try with your second Mockito expression.
To do what you want requires to mock myClass() first by returning a mocked class instance and mock isValidUser on that.
I have a class which I cannot easily use a dependency injection to mock due to an obligatory implementation of an interface - in a nutshell, for that reason I will be using Whitebox and my concern here is not related to the design, instead it is just to figure out how to properly "tearDown" the behavior caused by Whitebox. Bear with me for a second, I will give you more details - this is the main dummy class:
public class Dummy implements MandatoryInterface {
private static final Logger logger = Logger.getLogger(MethodHandles.lookup().lookupClass());
private final ObjectMapper mapper = new ObjectMapper();
#Override
public Object convertArgumentToJson(Object arg) {
if (arg != null) {
try {
return mapper.writeValueAsString(arg);
} catch (IOException e) {
// doSomething();
logger.error("Error tracking request", e);
}
}
return null;
}
}
Supposing that I want to cover what happens if an exception occurs here, the only way that I see is to use a Whitebox.setInternalState. Here the test:
public class DummyTest {
private Dummy dummy = new Dummy();
#Test
public void testA() throws IOException {
final ObjectMapper mock = Mockito.mock(ObjectMapper.class);
Whitebox.setInternalState(dummy, "mapper", mock);
Mockito.when(mock.writeValueAsString(Mockito.any()))
.thenThrow(new IOException());
Assert.assertNull(dummy.convertArgumentToJson("testA"));
}
#Test
public void testB() {
Assert.assertNotNull(dummy.convertArgumentToJson("testB"));
}
}
As you can see, I cannot define the mapper within the Dummy class as static, because of the Whitebox (it wouldn't work).
Having said that, after the execution of testA() we have the mapper being mocked:
The problem is: when executing the testB I don't want mock anymore - it should be the old instanced ObjectMapper initially included in Dummy. But what appears:
Now, my question:
What is the proper way to undo the
Whitebox.setInternalState(dummy, "mapper", mock);
P.S.: I have considered using a tearDown() like this:
#AfterMethod
public void tearDown(){
Whitebox.setInternalState(dummy, "mapper", originalState);
}
However, in this scenario, my pitest (mutation test) would consider that I'm not covering the initialization of ObjectMapper, so: is there a way just to undo the Whitebox for the rest of the tests without setting manually the old one?
Sorry for the long description and thanks in advance.
Regards,
Sorry guys, I managed to have it.
Just in case someone else could face the same question, the answer was easier than I supposed.
private static final String MAPPER_DESC = "mapper";
private ObjectMapper originalMapper;
#BeforeMethod
public void init() {
MockitoAnnotations.initMocks(this);
originalMapper = (ObjectMapper) Whitebox.getInternalState(converter, MAPPER_DESC);
}
#AfterMethod
public void tearDown() {
Whitebox.setInternalState(converter, MAPPER_DESC, originalMapper);
}
Then testA and testB can keep the same code. And the mutation test will still have the ObjectMapper attribute declaration covered as shown in the image:
I'm using Mosby and I would like to test my simple presenter.
public class DetailsPresenter extends MvpBasePresenter<DetailsView> {
public void showCountry(Country country) {
getView().setTitle(country.getName());
getView().setFlag(country.getFlagUrl());
}
}
I've tried to solve it by mocking Presenter:
public class DetailsPresenterTest {
private DetailsPresenter mockPresenter;
private DetailsView mockView;
#Before
public void setUp() throws Exception {
mockPresenter = mock(DetailsPresenter.class);
mockView = mock(DetailsView.class);
when(mockPresenter.isViewAttached()).thenReturn(true);
when(mockPresenter.getView()).thenReturn(mockView);
doCallRealMethod().when(mockPresenter).showCountry(any(Country.class));
}
#Test
public void shouldShowFlag() throws Exception {
mockPresenter.showCountry(any(Country.class));
verify(mockView, times(1)).setFlag(anyString());
}
#Test
public void shouldShowName() throws Exception {
mockPresenter.showCountry(any(Country.class));
verify(mockView, times(1)).setTitle(anyString());
}
}
But I've got the error
Wanted but not invoked:
detailsView.setFlag(<any string>);
-> at eu.szwiec.countries.details.DetailsPresenterTest.shouldShowFlag(DetailsPresenterTest.java:39)
Actually, there were zero interactions with this mock.
I've tried to use also real presenter without a success.
you have to use real Presenter and a real country object to invoke showCountry(). Everything else doesnt make sense (not testing the real presenter but a mock presenter instance).
#Test
public void showFlagAndName(){
DetailsView mockView = mock(DetailsView.class);
DetailsPresenter presenter = new DetailsPresenter();
Country country = new Country("Italy", "italyFlag");
presenter.attachView(mockView);
presenter.showCountry(country);
verify(mockView, times(1)).showCountry("Italy");
verify(mockView, times(1)).setFlag("italyFlag");
}
Have you tried to add some logging to find out what's going on?
I think you do not hit the real method as
mockPresenter.showCountry(any(Country.class));
does not construct a Country object instance but simply passes null. So the condition
doCallRealMethod().when(mockPresenter).showCountry(any(Country.class));
is not met. If you use a less strict condition
doCallRealMethod().when(mockPresenter).showCountry(any());
you should get a NullPointerException.
You may solve this by using a real or a mocked Country instance on your method invocation.
I have this code in a method I´m unittesting
public void SomeMethod()
{
IMyLogger log = new Logger();
log.ConfigLogger(); // Trying to not call this method
//...some other code...
}
Where this is the Logger class
public class Logger : IMyLogger
{
public void ConfigLogger()
{
//Serilog logging code I´m trying to not call in my unittests.
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.CreateLogger();
}
}
And this is the my unittest where I´m trying to mock away (not call the code inside of the ConfigLogger() method without luck.
public void Test()
{
var logger = A.Fake<IMyLogger>();
A.CallTo(() => logger.ConfigLogger()).DoesNothing(); //Not working..
}
So what I´m I missing here? Why do I think this should be just like this? Even though Serilog specialist give me a better way to unittest Serilog I would also like to find out how to "FakeItEasy a void method".
Your production code is still using a concrete instance of Logger. FakeItEasy cannot influence the behaviour of anything but FakeItEasy-created Fakes.
The pattern for use is:
create a Fake that your production code will use as a collaborator
configure the Fake
provide the Fake to an instance of the production class under test
execute the production code
optionally interrogate the Fake
As your production code is written now, there is no opportunity to inject a collaborator - it makes its own collaborator (log). In order to avoid calling Logger methods, you'll need to provide a way to inject an alternative IMyLogger.
You should avoid having to instantiate and configure your logger within the method.
Something like
public class SomeClass()
{
IMyLogger log;
public SomeClass(IMyLogger logger)
{
this.log = logger;
}
public void SomeMethod()
{
// code you want to test
}
}
Then just pass it into your class under test:
public void Test()
{
var logger = A.Fake<IMyLogger>();
var someClass = new SomeClass(logger);
// test someClass.SomeMethod()
}
I am trying to test my gwt app with gwt junit but seems to not be able to set up things correctly to make the objectify be tested.
All the tutorials demonstrate testing DataStore but not objectify (which is higher level of data base service)
My base class for testing looks like this:
public class TestBase {
private static final LocalServiceTestHelper helper =
new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig());
protected static ObjectifyFactory fact;
#BeforeClass
public static void setUp() {
helper.setUp();
fact = new ObjectifyFactory() {
#Override
public Objectify begin(ObjectifyOpts opts)
{
opts.setSessionCache(false);
return super.begin(opts);
}
};
}
#AfterClass
public static void tearDown() {
helper.tearDown();
}
}
then i have classes that extends the base:
public class UserServiceTest extends TestBase{
private User inactiveUser;
private UserService us;
Objectify _ofy;
#Rule
public ExpectedException thrown = ExpectedException.none();
#Before
public void beforeTest() {
//Register the classes used in the test
fact.register(User.class);
us = new UserService();
inactiveUser = new User();
}
#Test
public void basicTest(){
Objectify ofy = ObjectifyService.begin();
ofy.put(inactiveUser); //This fails with exception: An exception occurred: com.google.apphosting.api.ApiProxy$CallNotFoundException
//My goal is to reach these test but "addUser" uses also objectify
//UserService.addUser("shpungin#gmail.com", "bye");
//assertNotNull(inactiveUser.get_id());
}
Do you have an idea of what am I doing wrong? I looked all over the Internet and found no solution (some even said to remove app-engine-sdk from .classpath but it dosent seems to work.
Thank you.
I solved this.
Although com.google.apphosting.api.ApiProxy should be part of the app-engine
Some jars still needs to be inside the .classpath :
${SDK_ROOT}/lib/testing/appengine-testing.jar
${SDK_ROOT}/lib/impl/appengine-api.jar
${SDK_ROOT}/lib/impl/appengine-api-labs.jar
${SDK_ROOT}/lib/impl/appengine-api-stubs.jar //This one I missed
Also I upgraded my app-engine to v 1.6.4.1 (maybe that also helped).