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));
}
}
Related
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.
I am writing a unit test to mock a static method in the verticle but getting ClassNotPreparedException always. I think that its only possible to mock this way if only the class is static, but i have non static class. What am i missing?
I have tried various solutions like using #rule or #PowerMockIgnore
//myVerticleTest.java
package com.blabla.me.verticles;
import static com.google.common.truth.Truth.assertThat;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import io.vertx.core.Vertx;
import io.vertx.junit5.VertxTestContext;
import io.vulpx.VulpxTestBase;
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.junit.runner.RunWith;
import com.blabla.me.verticles.AdditionalInformationCardVerticle;
import org.powermock.modules.junit4.rule.PowerMockRule;
import org.junit.Rule;
import com.blabla.me.verticles.st;
#RunWith(PowerMockRunner.class)
#PrepareForTest({ st.class })
#PowerMockIgnore({"org.mockito.*"})
public class myVerticleTest extends VulpxTestBase {
#Rule public PowerMockRule rule = new PowerMockRule();
private Vertx vertx;
private AdditionalInformationCardVerticle dummy;
#BeforeEach
#PrepareForTest({ st.class })
public void setUp(VertxTestContext testContext) throws Exception {
vertx = Vertx.vertx();
try {
PowerMockito.mockStatic(st.class);
PowerMockito.when(st.createClient()).thenReturn("kk");
//deploying verticle
dummy = new AdditionalInformationCardVerticle();
vertx.deployVerticle(dummy, testContext.completing());
} catch (Exception e) {
System.out.println("heyyy eroorrr : " + e);
}
}
#Test
#PrepareForTest({ st.class })
public void justnormaltest() {
cla ownclass = new cla();
String k = ownclass.createfromclass();
assertThat("kk").isEqualTo(k);
}
}
// st.java
public class st {
public static String createClient() {
return "kk";
}
}
// cla.java
public class cla {
public String createfromclass() {
return st.createClient();
}
}
I expect it to run the assertion but i always get below excpetion:
"org.powermock.api.mockito.ClassNotPreparedException:
The class com.sap.me.verticles.st not prepared for test.
To prepare this class, add class to the '#PrepareForTest' annotation.
In case if you don't use this annotation, add the annotation on class or method level. "
Here:
#PrepareForTest({ st.class })
That one goes to exactly one place: in front of your test class public class myVerticleTest.
And hint: instead of adding more and more "things" to not working code: pick any good documentation, and try to follow that to the last ; in the example code (instead of assuming that adding more and more things here or there would help).
One good starting point: the official documentation on static mocking.
And of course, the usual caveat: consider not learning about PowerMock in the first place. Instead focus on writing "easy to test" code. Far too often, people think PowerMock(ito) is the answer to their problem. When their problem in reality is their inability to write "easy to test" production code.
I am writing JUnit test cases for my spring application. I use codepro tool in eclipse for generate test cases. when I run this test cases than it is run on JVM not on Tomcat server. so I want to know how it could be run on server? and which is best practice to run test cases on JVM or tomcat? and why? so please suggest me. code is as follow.
import java.io.InputStream;
import java.util.Properties;
import javax.servlet.http.HttpSession;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.bind.annotation.RequestMapping;
import com.zodiacapi.framework.business.ZodiacMobileBusinessTx;
import com.zodiacapi.framework.controller.ZodiacMobileAPIController;
import com.zodiacapi.framework.delegate.SendNotificationDelegate;
import com.zodiacapi.framework.dto.ReturnAPIMessageDTO;
import com.zodiacapi.framework.dto.UserDTO;
import com.zodiacweb.framework.cache.CacheService;
import com.zodiacweb.framework.cache.EhCacheServiceImpl;
import com.zodiacweb.framework.exception.ZodiacWebException;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "classpath:applicationContext.xml" })
public class ZodiacMobileAPIControllerTest extends TestCase {
private static final Logger logger = LoggerFactory.getLogger(ZodiacMobileAPIControllerTest.class);
#Autowired
private ZodiacMobileBusinessTx zodiabMobileBusinessTx;
public ZodiacMobileBusinessTx getZodiabMobileBusinessTx() {
return zodiabMobileBusinessTx;
}
#Test
public void testMobileLogin_1()
throws Exception {
ReturnAPIMessageDTO entities = new ReturnAPIMessageDTO();
Properties prop = new Properties();
InputStream in = getClass().getResourceAsStream("login.properties");
prop.load(in);
try{
UserDTO result = zodiabMobileBusinessTx.login(prop.getProperty("username"), prop.getProperty("password"), prop.getProperty("apikey"), prop.getProperty("deviceid"), prop.getProperty("deviceModel"));
System.out.println("result of test"+result);
} catch (ZodiacWebException e) {
logger.error("Internal Server Error fetching user info", e);
entities.setStatus("false");
entities.setMessage(e.getMessage());
entities.setVersion("");
} catch (Throwable t) {
entities.setStatus("false");
entities.setMessage(t.getMessage());
entities.setVersion("");
}
}
}
For a unit test you would usually execute it within the JVM. You would probably only execute Integration/Functional tests on an application running in a server.
The choices you have for testing a Spring Controller(That I am familiar with) are:
Test the controller as a regular POJO outside of the container and server
for example : MyController controller = new MyController())
Test the controller using Spring Test MVC. This will actually start up Spring during your tests.(I prefer this option) see Unit Test Spring Controllers for some examples.
If you want to test your application in a real tomcat instance you can use
Arquillian together with The Arquillian Spring Extension. This last option is definitely the most complex in terms of learning curve. But it's nice to be aware of.(Haven't successfully used it with a Spring Application myself)
Don't worry about using Arquillian for now ... it takes some time to learn.
See my code below for a working example of testing a spring controller. I noticed from your code sample that you did not have all the correct annotations and the initialization method.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = App.class)
#TestPropertySource(locations = "classpath:test.properties")
#WebAppConfiguration
public class AdminUserControllerUnitTest {
MockMvc mvc;
#Autowired
WebApplicationContext webApplicationContext;
#Before
public void initialize(){
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
public void testListUsers() throws Exception {
Account account = new Account();
account.setId(1l);
mvc.perform(
get("/admin/user")
.sessionAttr("account",account)
);
.andExpect(MockMvcResultMatchers.model().attribute("users",hasSize(4)));
}
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.)
I have many Test Suites with each one contains many Test Classes. Here is how they look like:
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
#RunWith(Suite.class)
#SuiteClasses( {ATest.class, BTest.class})
public class MyFirstTestSuite {
#BeforeClass
public static void beforeClass() throws Exception {
// load resources
}
#AfterClass
public static void afterClass() throws Exception {
// release resources
}
}
Sometimes I want to disable a whole Test Suite completely. I don't want to set each test class as #Ignore, since every test suite loads and releases resources using #BeforeClass and #AfterClass and I want to skip this loading/releasing when the test suite is ignored.
So the question is: is there anything similar to #Ignore that I can use on a whole Test Suite?
You can annotate the TestSuite with #Ignore.
#RunWith(Suite.class)
#SuiteClasses({Test1.class})
#Ignore
public class MySuite {
public MySuite() {
System.out.println("Hello world");
}
#BeforeClass
public static void hello() {
System.out.println("beforeClass");
}
}
doesn't produce any output.
SlowTest is a class defined by user. It can be empty (without any functions or attributes). You can name it whatever you want: