Is there any possibility to trigger a method whenever a testcase or assertion fails, in order to do some things when a testcase fails (e.g. Screenshot while UI-Testing, writing an error log, and so on...).
Maybe there is something like an annotation, I did not yet notice.
Thanks in advance!
You can use the TestWatcher rule and implement your own failed method to take a screenshot or whatever you need to do upon test failure. Slightly modified example from the official documentation:
public static class WatchmanTest {
private static String watchedLog;
#Rule
public TestRule watchman = new TestWatcher() {
#Override
protected void failed(Throwable e, Description description) {
watchedLog += d + "\n";
// take screenshot, etc.
}
};
#Test
public void fails() {
fail();
}
}
Related
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:
Here is my test method where It should be success if showLoading() and loadDataSuccess(response) was called:
#RunWith(PowerMockRunner.class)
public class PresenterTest {
#Mock
private ProfileContract.View view;
#Mock
private ProfileContract.Handler handler;
#Test
public void onLoadDataClicked() {
presenter.loadData();
verify(mView, times(1)).showLoading();
verify(mHandler, times(1)).loadDataSuccess();
}
}
UPDATE 1
Here is my presenter:
class ProfilePresenter(private val mView: ProfileContract.View) : ProfileContract.Handler {
override fun loadData() {
mView.showLoading()
mUserService.user()
.compose(RxUtil.mapper())
.subscribe({ response ->
loadDataSuccess()
}, { error ->
//stuff
})
}
}
Thanks!
If you use return statment, your test finish with success status.
I think there is a basic problem with your test setup:
You do not use verify to check if one function calles another function within the same class. Verify is used to verify that the tested class calls function on other (mocked) classes. If I am not mistaken, your setup should actually give you an error message saying that you can not use verify on instantiated classes.
What you should do -if you want to check if onCompleteClicked() produces the correct results- is to check if the data that gets changed inside the onStuffComplete() function is set correctly. You can use an assert for that.
As an example, lets say onStuffCompleted() sets completeCounter to 1
#Test
public void onCompleteClicked() {
presenter.onStuffCompleteClicked();
assertEquals(completCounter , 1);
}
And to answer your original question: verify (and assert) will pass if the requirements were met (and by this the whole test will pass) and fail if not. You do not need to add any additional stuff (but once again: verify will only work with mocked classes).
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'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.
I have the following four classes: DataConsumer, DataProducer, SomeQualifier, a META-INF/beans.xml and a test. The class files are coded as follows:
public class DataConsumer {
private boolean loaded = false;
#Inject
#SomeQualifier
private String someString;
public void afterBeanDiscovery(
#Observes final AfterBeanDiscovery afterBeanDiscovery,
final BeanManager manager) {
loaded = true;
}
public boolean getLoaded() {
return loaded;
}
public String sayHello() {
return someString;
}
}
public class DataProducer {
#Produces
#SomeQualifier
private final String sample = "sample";
}
public #interface SomeQualifier {
}
The unit test looks like this.
public class WeldTest {
#Test
public void testHelloWorld() {
final WeldContainer weld = new Weld().initialize();
final DataConsumer consumer = weld.instance()
.select(DataConsumer.class).get();
Assert.assertEquals("sample", consumer.sayHello());
Assert.assertTrue(consumer.getLoaded());
}
}
However, it is failing on the assertTrue with getLoaded() it appears that the #Observes does not get fired.
Take a look at arquillian: www.arquillian.org. It'll take care of all of this for you.
I found a similar question that had answered my question
CDI - Observing Container Events
Although I am unable to use DataConsumer as both an Extension and a CDI managed bean. So it needs a third class just to be the Extension. However, because Extension have no access to managed beans since they are not created yet, I conclude that is no possible solution to use an #Observes AfterBeanDiscovery to modify the bean data. Even the BeanManager that gets passed in cannot find any of the beans.