Espresso 2.0 - Method annotated with #Test inside class extending junit3 testcase - java

I got a weird warning Method annotated with #Test inside class extending junit3 testcase when using the new ActivityInstrumentationTestCase2 class shipped with Espresso 2.0.
My class looks just like the one that Google provided as an example:
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.LargeTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static android.support.test.espresso.matcher.ViewMatchers.assertThat;
import static org.hamcrest.Matchers.notNullValue;
#RunWith(AndroidJUnit4.class)
#LargeTest
public class MyCoolActivityTests extends ActivityInstrumentationTestCase2<MyCoolActivity> {
private MyCoolActivity mActivity;
public MyCoolActivityTests() {
super(MyCoolActivity.class);
}
#Before
public void setUp() throws Exception {
super.setUp();
injectInstrumentation(InstrumentationRegistry.getInstrumentation());
mActivity = getActivity();
}
#Test
public void checkPreconditions() {
assertThat(mActivity, notNullValue());
// Check that Instrumentation was correctly injected in setUp()
assertThat(getInstrumentation(), notNullValue());
}
#After
public void tearDown() throws Exception {
super.tearDown();
}
}
I've added all necessary things to the build.gradle:
android {
defaultConfig {
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
}
dependencies {
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.0'
androidTestCompile 'com.android.support.test:testing-support-lib:0.1'
}
Is there any way to get this warning away?

ActivityInstrumentationTestCase2 is a JUnit 3 test case because it extends from TestCase.
#Test annotation is a replacement for the test-prefix naming convention used in JUnit 3. JUnit 4 test classes no longer require to extend TestCase or any of its subclasses. In fact JUnit 4 tests cannot extend TestCase, otherwise AndroidJUnitRunner will treat them as JUnit 3 tests.
http://developer.android.com/tools/testing-support-library/index.html#AndroidJUnitRunner
You could either migrate to ActivityTestRule provided by com.android.support.test:rules:0.4 (or later), or stick with JUnit 3.
Another option is InstrumentationRegistry, provided by Espresso 2, which has getInstrumentation(), getContext(), getTargetContext() (and more). These methods provide access to the current instrumentation, test context, and target context in a static manner. This makes it possible to write your own static utility methods for use in JUnit 4 test case classes. These utilities would mimic functionality that is currently only available in the base JUnit 3 test case classes. (This is no longer necessary.)

Related

Junit test case in spring boot

How can i write junit test for void method?
i have following method in service layer
#Override
public void add(Demo demo) throws ApiError {
if (!repository.existsByNameAndAge(demo.getName(), demo.getAge())) {
throw new ApiError(HttpStatus.BAD_REQUEST, "bad request");
}
Integer count = newRepository.countByName(cart.getName());
newRepository.save(new Demo(demo.getName(), demo.getAge(), demo.getCity(), count));
}
here is my service method and i want to do junit test case for it. but it's return type is void. i want to do testing of each statment. how can i done junit testing of this please suggest me..
Sorry I wrote the answer for Junit5 and then noticed you tagged Junit4, I will post it anyway, the idea is the same and differences in the code should be minor. What you can do is using Mockito to inject mocks and verify that the methods are called with the parameters you expect them be called. I would write 2 test cases: one to check the exception is thrown and the repositories are not called and another one to check the repository is properly saving:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.InjectMocks;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.*;
#ExtendWith(MockitoExtension.class)
class MyServiceTest {
#Mock
private Repo repository;
#Mock
private NewRepo newRepository;
#Captor
private ArgumentCaptor<Demo> demoCaptor;
#InjectMocks
private MyService service;
#Test
void throwsIfDoesNotExistForGivenNameAndAge() {
when(repository.existsByNameAndAge("name", 12)).thenReturn(false);
assertThrows(ApiError.class, () -> service.add(new Demo("name", 12, "city", 10)));
verify(newRepository, times(0)).countByName(anyString());
verify(newRepository, times(0)).save(any(Demo.class));
}
#Test
void savesToNewRepositoryWithRightValues() {
when(repository.existsByNameAndAge("name", 12)).thenReturn(true);
when(newRepository.countByName("cart")).thenReturn(10);
service.add(new Demo("name", 12, "city", 10));
verify(newRepository, times(1)).save(demoCaptor.capture());
final Demo actual = captor.getValue();
final Demo expected = //create your expected here
assertEquals(expected, actual);
}
Remember to implement equals() and hashCode() in your Demo class, or another option could be asserting on the fields of Demo you care about. I'm also not sure what cart on which you are calling getName() is, but if it's another dependency of your service you will have to inject it as a mock and properly set it up with when() and return value.
The differences in terms of junit4/5 should be (not 100% sure it's all of them, going with my memory here):
the imports
the #ExtendWith should be #RunWith(mockitojunitrunner.class)
the test for the exception should be #Test(expected = ApiError.class) instead of using assertThrows
This function basically saves the data if the data is not available in the repository, Junits are meant to check if this function is working as expected. Here you will test for 2 cases
when data is available in the repository: For this mock repository.existsByNameAndAge(...) and return false
,in test case use expected #Test(expected=ApiError.class)
when it is not: In this case use opposite of the above case and don't use the expected attribute.

How to test main class of Spring-boot application

I have a spring-boot application where my #SpringBootApplication starter class looks like a standard one. So I created many tests for all my functionalities and send the summary to sonarqube to see my coverage.
For my starter class Sonarqube tells me that I just have 60% coverage. So the average coverage is not good as expected.
My Test class is just the default one.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = ElectronicGiftcardServiceApplication.class)
public class ElectronicGiftcardServiceApplicationTests {
#Test
public void contextLoads() {
}
}
So how can I test my main class in the starter class of my application?
All these answers seem overkill.
You don't add tests to make a metric tool happy.
Loading a Spring context of the application takes time. Don't add it in each developer build just to win about 0.1% of coverage in your application.
Here you don't cover only 1 statement from 1 public method. It represents nothing in terms of coverage in an application where thousands of statements are generally written.
First workaround : make your Spring Boot application class with no bean declared inside. If you have them, move them in a configuration class (for make them still cover by unit test). And then ignore your Spring Boot application class in the test coverage configuration.
Second workaround : if you really need to to cover the main() invocation (for organizational reasons for example), create a test for it but an integration test (executed by an continuous integration tool and not in each developer build) and document clearly the test class purpose :
import org.junit.Test;
// Test class added ONLY to cover main() invocation not covered by application tests.
public class MyApplicationIT {
#Test
public void main() {
MyApplication.main(new String[] {});
}
}
You can do something like this
#Test
public void applicationContextLoaded() {
}
#Test
public void applicationContextTest() {
mainApp.main(new String[] {});
}
I solved in a different way here. Since this method is there only as a bridge to Spring's run, I annotated the method with #lombok.Generated and now sonar ignores it when calculating the test coverage.
Other #Generated annotations, like javax.annotation.processing.Generated or javax.annotation.Generated might also work but I can't test now because my issue ticket was closed.
package com.stackoverflow;
import lombok.Generated;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class Application {
#Generated
public static void main(String... args) {
SpringApplication.run(Application.class, args);
}
}
I had the same goal (having a test that runs the main() method) and I noticed that simply adding a test method like #fg78nc said will in fact "start" the application twice : once by spring boot test framework, once via the explicit invocation of mainApp.main(new String[] {}), which I don't find elegant.
I ended up writing two test classes : one with #SpringBootTest annotation and the empty test method applicationContextLoaded(), another one without #SpringBootTest (only RunWith(SpringRunner.class)) that calls the main method.
SpringBootApplicationTest
package example;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.boot.test.context.SpringBootTest;
#RunWith(SpringRunner.class)
#SpringBootTest
public class SpringBootApplicationTest {
#Test
public void contextLoads() {
}
}
ApplicationStartTest
package example;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
public class ApplicationStartTest {
#Test
public void applicationStarts() {
ExampleApplication.main(new String[] {});
}
}
Overall, the application is still started two times, but because there is now two test classes. Of course, with only these two tests methods, it seems overkill, but usually more tests will be added to the class SpringBootApplicationTest taking advantage of #SpringBootTest setup.
In addition to the answers above, here is a unit test of a SpringBoot application's main method for if you are using JUnit 5 and Mockito 3.4+:
try (MockedStatic<SpringApplication> mocked = mockStatic(SpringApplication.class)) {
mocked.when(() -> { SpringApplication.run(ElectronicGiftCardServiceApplication.class,
new String[] { "foo", "bar" }); })
.thenReturn(Mockito.mock(ConfigurableApplicationContext.class));
ElectronicGiftCardServiceApplication.main(new String[] { "foo", "bar" });
mocked.verify(() -> { SpringApplication.run(ElectronicGiftCardServiceApplication.class,
new String[] { "foo", "bar" }); });
}
It verifies that the static method run() on the SpringApplication class is called with the expected String array when we call ElectronicGiftCardServiceApplication.main().
Same idea as awgtek and Ramji Sridaran, but their solutions are for JUnit 4.
You can Mock SpringApplication since that is a dependency of the method under test. See how here.
I.e.
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.springframework.boot.SpringApplication;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.verifyStatic;
#RunWith(PowerMockRunner.class)
public class ElectronicGiftcardServiceApplicationTest {
#Test
#PrepareForTest(SpringApplication.class)
public void main() {
mockStatic(SpringApplication.class);
ElectronicGiftcardServiceApplication.main(new String[]{"Hello", "World"});
verifyStatic(SpringApplication.class);
SpringApplication.run(ElectronicGiftcardServiceApplication.class, new String[]{"Hello", "World"});
}
}
Using junit
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.springframework.boot.SpringApplication;
import static org.assertj.core.api.Assertions.*;
class WebsiteApplicationTests {
#Test
void testApplication() {
MockedStatic<SpringApplication> utilities = Mockito.mockStatic(SpringApplication.class);
utilities.when((MockedStatic.Verification) SpringApplication.run(WebsiteApplication.class, new String[]{})).thenReturn(null);
WebsiteApplication.main(new String[]{});
assertThat(SpringApplication.run(WebsiteApplication.class, new String[]{})).isEqualTo(null);
}
}
Add these dependencies in pom.xml
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.8.0</version>
<scope>test</scope>
</dependency>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>your.awesome.package.Application</mainClass>
</configuration>
</plugin>
If you aim for 100% coverage, one thing you can do is simply not having a main method at all. You still require a class annotated with #SpringBootApplication but it can be empty.
Be warned though as it has its drawbacks and other tools that rely on main can break.
This simple mock test for SpringApplication does not invoke any methods but just tests the starter app. [uses PowerMockRunner.class]
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.springframework.boot.SpringApplication;
#RunWith(PowerMockRunner.class)
#PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*", "javax.management.*"})
public class JobsAppStarterTest {
#Test
#PrepareForTest(SpringApplication.class)
public void testSpringStartUp() {
PowerMockito.mockStatic(SpringApplication.class);
SpringApplication.run(JobsAppStarter.class, new String[] {"args"});
JobsAppStarter.main(new String[] {"args"});
}
}
If the idea is to exclude the SpringApplication class from sonar scan (which is the recommended way of doing it), you can exclude it with the following configuration in the build.gradle
plugins {
id 'org.sonarqube' version '3.4.0.2513'
}
sonarqube {
properties {
property "sonar.exclusions", "**/*Application.java"
}
}
Even though this question has been answered extensively I had a use case that is not covered here that is perhaps interesting to share. I am validating some properties at startup and I wanted to assert that the application would fail to start if these properties were configured wrong. In JUnit4 I could have done something like this:
#ActiveProfiles("incorrect")
#SpringBoot
public class NetworkProbeApplicationTest {
#Test(expected=ConfigurationPropertiesBindException.class)
public void contextShouldNotLoadWhenPropertiesIncorrect() {
}
}
But in JUnit5 you can no longer add the "expected" value to your #Test annotation and you have to do it differently. And since I wanted to start the application with an incorrect set of properties I needed to pass in which profile to use as a main() argument. I could not really find this documented anywhere, but passing in arguments through the main() method requires you to prefix your arguments with a double hyphen and separate the key and value with an equals sign. A complete test would look like this:
import org.junit.jupiter.api.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesBindException;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class NetworkProbeApplicationTest {
#Test
public void contextShouldNotLoadWhenPropertiesIncorrect() {
Exception exception = assertThrows(ConfigurationPropertiesBindException.class, () -> {
SpringApplication.run(NetworkProbeApplication.class, "--spring.profiles.active=incorrect");
});
String expectedMessage = "Error creating bean with name 'dnsConfiguration': Could not bind properties to 'DnsConfiguration' : prefix=dns";
assertTrue(exception.getMessage().contains(expectedMessage));
}
}

How to prevent Mockito mocking ... dynamically?

This is a follow-on question to this one.
My problem is: I upgraded our environment to newer versions of JUnit, Mockito, ... Unfortunately, I was running my local tests with a up-to-date IBM JRE. Then we found out that in our build environment, all our Mockito tests are now failing with
org.mockito.exceptions.base.MockitoException:
Mockito cannot mock this class: interface java.util.concurrent.ExecutorService.
Mockito can only mock non-private & non-final classes.
If you're not sure why you're getting this error, please report to the mailing list.
Followed by:
Early IBM virtual machine are known to have issues with Mockito, please upgrade to an up-to-date version.
OK; so I spent some time to A) figure within a test that it is run by an outdated JRE so I could B) then have all tests skipped. In the end; I put together the below code.
But some notes upfront: I verified that my forceJUnit...() method really throws that exception when running with an IBM Java8 JRE that has SR1 or SR2 level. My point is: although the method should throw ... when I run that test case, I am still hitting the Mockito exception about "can not mock"!
Any idea what I am doing wrong here?
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
class Helper {
public static void forceJUnitToIgnoreTestForJava8SR1() {
Assume.assumeFalse("Ignoring test when running with JRE SR1", isJava8SR1());
}
private static boolean isJava8SR1() {
String fullVersion = System.getProperty("java.fullversion", "");
return fullVersion.contains("R28_Java8_SR1");
}
}
#RunWith(MockitoJUnitRunner.class)
public class Mcve {
#Mock
private ExecutorService service;
#Before
public void setup() {
Helper.forceJUnitToIgnoreTestForJava8SR1();
// make the mcve do something
doAnswer(new Answer<Future<?>>() {
#Override
public Future<?> answer(InvocationOnMock invocation) throws Throwable {
return null;
}
}).when(service).submit(any(Runnable.class));
}
#Test
public void test() {
System.out.println("won't show up ;-(");
}
}
Directly call MockitoAnnotations.initMocks(this) instead of using MockitoJUnitRunner:
public class Mcve {
#Mock
private ExecutorService service;
#Before
public void setup() {
Helper.forceJUnitToIgnoreTestForJava8SR1();
MockitoAnnotations.initMocks(this);
...
}
#Test
public void test() {
System.out.println("won't show up ;-(");
}
}
Not sure here, but:
Maybe you just try to call Helper.forceJUnitToIgnoreTestForJava8SR1(); within a method marked with #BeforeClass
Or, you could change your test case and remove the #Mock annotation; and do ALL of the required configuration steps AFTER calling Helper.forceJUnitToIgnoreTestForJava8SR1(); within your setup method?

Mock function not found in EasyMock

I am trying to build my first EasyMock test, however I have a trivial problem that the function "mock" is not found.
Here is my pretty straightforward code:
package homework;
import org.easymock.EasyMockSupport;
import org.junit.Before;
import org.junit.Test;
import com.locusenergy.homework.Elevator;
public class ElevatorTest extends EasyMockSupport{
private Elevator elevator;
#Before
public void setUp() {
elevator = mock(Elevator.class);
}
#Test
public void testCallElevator() {
elevator.requestFloor(5);
}
}
However, I am getting an error that mock is not found. I have no idea how to fix this issue.
Your code probably will work as it is when the next version of EasyMock is released.
The user guide refers to the mock method which can be found in the master branch but is not in the latest release (3.3.1).
Depending on Documentationof EasyMock the example should look like this:
package homework;
import org.easymock.EasyMockSupport;
import org.junit.Before;
import org.junit.Test;
import com.locusenergy.homework.Elevator;
public class ElevatorTest extends EasyMockSupport{
private Elevator elevator;
#Before
public void setUp() {
elevator = createMock(Elevator.class);
}
#Test
public void testCallElevator() {
elevator.requestFloor(5);
}
}
As described here: http://easymock.org/api/org/easymock/EasyMockSupport.html
The API Docs say the methods nameis createMock thats all.
Just sharing another approach...
You can use org.easymock.IMocksControl.
Your code will look something like below
....
#Before
public void setUp() {
IMocksControl mocksControl = createControl();
elevator = = mocksControl.createMock(Elevator.class);
}
.....
I prefer using IMocksControl. As the name\api suggest controls the behavior of its associated mock object.
Note : I am using easymock version 3.1
Regards,
MB

How to run PowerMock on dynamically created TestCase

I was trying to mock my test suites. My test framework creates test cases by scanning test files on disk. So each time the test cases are dynamically created.
I was trying to use PowerMock. Below is the thing I tried first.
public class GroupTestcase_T extends TestSuite {
static void run() {
scan();
junit.textui.TestRunner.run(g);
}
static void scan() {
// scan disk
for (MyTestCase t : tests) { addTest(t); }
}
}
#RunWith(PowerMockRunner.class)
#PrepareForTest(ClassToStub.class)
public class MyTestCase extends TestCase {
public MyTestCase(TestInfo info) {...}
#Override
protected void setUp() throws Exception {
PowerMockito.mockStatic(ClassToStub.class);
when(ClassToStub.methodToStub())
.thenReturn(new FakeProxy());
}
#Test
public void test() throws Exception {
// Test!
}
}
Above code seems not working:
Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods.
Those methods cannot be stubbed/verified.
2. inside when() you don't call method on mock but on some other object.
3. the parent of the mocked class is not public.
It is a limitation of the mock engine.
I traced the code and found that PowerMockRunner are not called at all.
Also I tried manually force Junit to run it with PowerMockRunner:
Result result = junit.run(new PowerMockRunner(MyTestCase.class));
PowerMockRunner has only one constructor that takes the test class as parameter. My test cases are different each time but all share the same class.
Any idea how to use PowerMock if TestCase are dynamically created?
I was using Junit 4 / PowerMock 1.5
You can generate your tests with the parameterized tests feature and apply the #PowerMockRule.
import static org.junit.Assert.assertTrue;
import java.util.Arrays;
import java.util.Collection;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.rule.PowerMockRule;
#RunWith(Parameterized.class)
#PrepareForTest(ClassToStub.class)
public class MyTestCase{
#Parameters
public static Collection<Object[]> scan() {
return Arrays.asList(new Object[][] {
{ new TestInfo() }, { new TestInfo() } });
}
#Rule
public PowerMockRule rule = new PowerMockRule();
public MyTestCase(TestInfo info) {
// ...
}
#Test
public void test() throws Exception {
PowerMockito.mockStatic(ClassToStub.class);
PowerMockito.when(ClassToStub.methodToStub()).thenReturn(new FakeProxy());
assertTrue(ClassToStub.methodToStub() instanceof FakeProxy);
}
}
Beware, in your example, you are mixing junit 3 (extends TestSuite, protected setUp) and junit 4 (#Test) test definitions.

Categories