I write function in rest to automatically generate user with admin role. Here's the function:
UserController.java
#RestController
#RequestMapping("users")
public class UserController {
#Autowired
private UserRepository userRepo;
#Autowired
private TokenRepository tokenRepo;
#GetMapping("admin")
public String getAdmin () {
JSONObject report = new JSONObject();
String dataAdmin = userRepo.findByUsername("admin");
if(dataAdmin == null) {
User myadmin = new User();
myadmin.setUsername("admin");
myadmin.setFirstname("admin");
myadmin.setLastname("admin");
myadmin.setEmail("admin#admin");
myadmin.setRole("admin");
userRepo.save(myadmin);
report.put("message", "admin generated");
} else {
report.put("message", "admin only generated once");
}
return report.toString();
}
I am trying to follow the instruction from here https://www.springboottutorial.com/unit-testing-for-spring-boot-rest-services.
In Unit Testing Http Get Operation section. I am getting several problem and also trying the different solution until I meet this Unit testing a Get request in a spring project from stackoverflow.
below is the testing script I've make so far.
package blablaaa.order;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONObject;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import blablaaa.order.controller.UserController;
import blablaaa.order.dao.UserRepository;
import blablaaa.order.model.User;
//#ExtendWith(SpringExtension.class)
//#SpringBootTest
#WebMvcTest(value = UserController.class)
class OrderApplicationTests {
//
#Autowired
private MockMvc mockMvc;
#MockBean
private UserRepository userRepo;
#Test
void contextLoads() throws Exception{
User myadmin = new User();
myadmin.setUsername("admin");
myadmin.setFirstname("admin");
myadmin.setLastname("admin");
myadmin.setEmail("admin#admin");
myadmin.setRole("admin");
List<User> myUser = new ArrayList<>();
myUser.add(myadmin);
RequestBuilder rb = MockMvcRequestBuilders.get("/users/admin").accept(MediaType.APPLICATION_JSON);
MvcResult result = mockMvc.perform(rb).andReturn();
JSONObject expect = new JSONObject();
expect.put("message", "admin generated");
// System.out.println(result.toString());
System.out.println(expect.toString());
// Assertions.assertTrue(result.toString().contains(expect.toString()));
}
}
I dont know, how the testing should be written. any keyword related to this?
[update]
Here's my main:
// OrderApplication.java
#SpringBootApplication
#EnableMongoRepositories("blablaaa.order.dao")
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
Here's my program error log
Description:
Field tokenRepo in blablaaa.order.controller.UserController required a bean named 'mongoTemplate' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean named 'mongoTemplate' in your configuration.
The first thing you have to do is to clearly understand what your method getAdmin is doing and secondly clearly identify your dependencies and how they interact in the main method.
To finish, you have to define what you want to test, in your case you have two cases. The if case and the else case.
In your test you must define the behavior of your mock with a when().thenReturn()...
Your test method will look like this:
#WebMvcTest(value = UserController.class)
class OrderApplicationTests {
#Autowired
private MockMvc mockMvc;
#MockBean
private UserRepository userRepo;
#Test
void contextLoads_whenNotNull() throws Exception{
User myadmin = new User();
myadmin.setUsername("admin");
myadmin.setFirstname("admin");
myadmin.setLastname("admin");
myadmin.setEmail("admin#admin");
myadmin.setRole("admin");
when(userRepo).thenReturn(myadmin)
mockMvc.perform(get("/users/admin")
.contentType(MediaType.APPLICATION_JSON))
.andExpectAll(
status().isOk(),
jsonPath("$.message", containsString("admin generated"))
);
}
#Test
void contextLoads_whenIsNull() throws Exception{
when(userRepo).thenReturn(null)
mockMvc.perform(get("/users/admin")
.contentType(MediaType.APPLICATION_JSON))
.andExpectAll(
status().isOk(),
jsonPath("$.message", containsString("admin only generated once"))
);
}
}
For more details: https://www.bezkoder.com/spring-boot-webmvctest/
previously i am laravel user. When i run unit testing, i dont need to run the apps before i can run testing scenario. So i think, the error log tell me that the mongo database is not connected to apps, even the apps itself is not running yet.
So, based on my case, i need to make sure run the apps first, not build. If i only build the apps, the program will remain error.
By default, when you need to build the apps, you need to make sure the apps is already running too. Spring Boot will also run the test case before build the jar file.
first, run the apps
mvn spring-boot:run
then,
you can run only the test case
mvn test
or build your apps
mvn clean package
Related
When I try using the below code to test the controllers no errors happen, it just says terminated with no logged messages or anything.
import static org.junit.Assert.assertEquals;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import ....controllers.UserController;
import ....data.response.UserResponse;
import ....models.user.User;
#RunWith(SpringRunner.class)
#WebMvcTest(UserController.class)
public class UserWebMvc {
#Autowired
private MockMvc mvc;
#Test
public void givenEmployees_whenGetEmployees_thenReturnJsonArray() throws Exception {
User alex = new User();
List<UserResponse> allUsers = Arrays.asList(new UserResponse(alex.getId(), alex.getInfo()));
RequestBuilder request = MockMvcRequestBuilders.get("/hello");
MvcResult result = mvc.perform(request).andReturn();
assertEquals(result.getResponse().getContentAsString(), "hello");
// userService.createUser(
// new UserRequest(alex.getInfo().getName(), alex.getInfo().getEmail(), alex.getInfo().getPassword()));
//
}
}
However my test to check that junit is working runs fine, so I'm thinking its something to do with SpringRunner or WebMvc
#SpringBootTest
class BackendApplicationTests {
#Test
void contextLoads() {
assertTrue(false);
}
}
Isn't assertTrue(false) always going to be false? You are basically checking whether false equals true, which will always fail.
Also, from the Spring documentation regarding #WebMvcTest:
Using this annotation will disable full auto-configuration and instead apply only configuration relevant to MVC tests (i.e. #Controller, #ControllerAdvice, #JsonComponent, Converter/GenericConverter, Filter, WebMvcConfigurer and HandlerMethodArgumentResolver beans but not #Component, #Service or #Repository beans).
Using this annotation, it will not scan any beans from the service layer. So if you have a UserService which is injected into the UserController, it will not be found by Spring.
When I run tests like this, I would do something like:
class UserControllerTest {
#InjectMocks
private UserController userController;
private MockMvc mockMvc;
#BeforeEach
public void setUp() {
MockitoAnnotations.openMocks(this);
mockMvc =
MockMvcBuilders.standaloneSetup(userController).build();
}
#Test
public void givenEmployees_whenGetEmployees_thenReturnJsonArray()
throws Exception {
User alex = new User();
List<UserResponse> allUsers = Arrays.asList(new
UserResponse(alex.getId(), alex.getInfo()));
RequestBuilder request = MockMvcRequestBuilders.get("/hello");
MvcResult result = mvc.perform(request).andReturn();
assertEquals(result.getResponse().getContentAsString(),
"hello");
}
}
I was using the wrong import. Changing to this fixed it
import static org.junit.Assert.assertTrue;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
I am writing some mockMvc unit tests for my actuator. I currently have one for health, which works fine
class HealthTest {
#Autowired
private Mockmvc mockMvc;
private ResultActions resultActions;
#BeforeEach() throws Exception {
resultActions = mockMvc.perform(get("/actuator/health"));
}
#Test
void shouldReturnOk() throws Exception {
resultActions.andExpect(jsonPath("status", is("UP")));
}
}
This works fine. However, when apply the same logic to "/actuator/info" (literally the exact same as the health class, with only that path changed (with config defined in the application.yml, and I have reviewed this manually, it's there when I run the application) I get a 200 status back, but no JSON, even though the web page itself shows a JSON object. It's like when I run it through this, it gets a blank page back, or the page, but not in json format.
Edit: So the config is in the main/application.yml. When I replicate the config to the test/application.yml, it works. Is there a way to get mvc to point to my main application.yml? As all this really tests is my duplicated test config
Edit 2: Better formatting of comment:
management:
endpoints:
web:
exposure:
include:
- info
info:
application:
name: My application name
/actuator/info provides your customized information. The default is empty information. Therefore, you must create a Spring bean to provide this information, for example:
import java.util.HashMap;
import java.util.Map;
import org.springframework.boot.actuate.info.Info;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.stereotype.Component;
#Component
public class BuildInfoContributor implements InfoContributor {
#Override
public void contribute(Info.Builder builder) {
Map<String, String> data = new HashMap<>();
data.put("version", "2.0.0.M7");
builder.withDetails(data);
}
}
And test:
#SpringBootTest
#AutoConfigureMockMvc
class Test {
#Autowired
private MockMvc mockMvc;
private ResultActions resultActions;
#BeforeEach()
void setUp() throws Exception {
resultActions = mockMvc.perform(MockMvcRequestBuilders.get("/actuator/info"));
}
#Test
void shouldReturnOk() throws Exception {
resultActions.andExpect(jsonPath("version", is("2.0.0.M7")));
}
}
Problem solved. So it turns out the test resources file can't be called application.yml, it needs it's own profile, or it totally overrides the main one.
Situation and Problem: In Spring Boot, how can I inject one or more mocked classes/beans into the application to do an integration test? There are a few answers on StackOverflow, but they are focused on the situation before Spring Boot 1.4 or are just not working for me.
The background is, that in the code bellow the implementation of Settings relies on third party servers and other external systems. The functionality of Settings is already tested in a unit test, so for a full integration test I want to mock out the dependency to these servers or system and just provide dummy values.
MockBean will ignore all existing bean definitions and provide a dummy object, but this object doesn't provide a method behavior in other classes that inject this class. Using the #Before way to set the behavior before a test doesn't influence the injected object or isn't injected in other application services like AuthenticationService.
My question: How can I inject my beans into the application context?
My test:
package ch.swaechter.testapp;
import ch.swaechter.testapp.utils.settings.Settings;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.test.context.junit4.SpringRunner;
#TestConfiguration
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {MyApplication.class})
public class MyApplicationTests {
#MockBean
private Settings settings;
#Before
public void before() {
Mockito.when(settings.getApplicationSecret()).thenReturn("Application Secret");
}
#Test
public void contextLoads() {
String applicationsecret = settings.getApplicationSecret();
System.out.println("Application secret: " + applicationsecret);
}
}
And bellow a service that should use the mocked class, but doesn't receive this mocked class:
package ch.swaechter.testapp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#Service
public class AuthenticationServiceImpl implements AuthenticationService {
private final Settings settings;
#Autowired
public AuthenticationServiceImpl(Settings settings) {
this.settings = settings;
}
#Override
public boolean loginUser(String token) {
// Use the application secret to check the token signature
// But here settings.getApplicationSecret() will return null (Instead of Application Secret as specified in the mock)!
return false;
}
}
Looks like you are using Settings object before you specify its mocked behavior.
You have to run
Mockito.when(settings.getApplicationSecret()).thenReturn("Application Secret");
during configuration setup. For preventing that you can create special configuration class for test only.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {MyApplication.class, MyApplicationTest.TestConfig.class})
public class MyApplicationTest {
private static final String SECRET = "Application Secret";
#TestConfiguration
public static class TestConfig {
#Bean
#Primary
public Settings settingsBean(){
Settings settings = Mockito.mock(Settings.class);
Mockito.when(settings.getApplicationSecret()).thenReturn(SECRET);
Mockito.doReturn(SECRET).when(settings).getApplicationSecret();
return settings;
}
}
.....
}
Also I would recommend you to use next notation for mocking:
Mockito.doReturn(SECRET).when(settings).getApplicationSecret();
It will not run settings::getApplicationSecret
When you annotate a field with #MockBean, spring will create a mock of the annotated class and use it to autowire all beans of the application context.
You must not create the mock yourself with
Settings settings = Mockito.mock(Settings.class);
this would create a second mock, leading to the described problem.
Solution :
#MockBean
private Settings settings;
#Before
public void before() {
Mockito.when(settings.getApplicationSecret()).thenReturn("Application Secret");
}
#Test
public void contextLoads() {
String applicationsecret = settings.getApplicationSecret();
System.out.println("Application secret: " + applicationsecret);
}
We are currently using Spring Boot to connect to a mocked local instance of Amazon SQS. The application itself is working when run, but we would like to try and test the SQS Config class, if possible and if it makes sense.
Here is the configuration class. All properties are pulled from the typical application.properties file when the Spring application itself is run.
import com.amazonaws.services.sqs.AmazonSQSAsync;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.aws.core.env.ResourceIdResolver;
import org.springframework.cloud.aws.messaging.core.QueueMessagingTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class AWSSQSConfig {
#Value("${aws.sqs.endpoint}")
private String AWSSqsEndpoint;
// Producer QueueMessageTemplate
#Bean
public QueueMessagingTemplate queueMessagingTemplate(AmazonSQSAsync amazonSqs, ResourceIdResolver resourceIdResolver) {
if (!AWSSqsEndpoint.isEmpty())
amazonSqs.setEndpoint(AWSSqsEndpoint);
return new QueueMessagingTemplate(amazonSqs, resourceIdResolver);
}
}
Here is the test class. We are attempting to pass the configuration in via TestPropertySource, but they don't actually seem to get to the AWSSQSConfig class. AWSSqsEndpoint inside the instance of the class is always NULL.
import com.amazonaws.services.sqs.AmazonSQSAsync;
import com.lonewolf.formsbuilder.config.AWSSQSConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.aws.core.env.ResourceIdResolver;
import org.springframework.cloud.aws.messaging.core.QueueMessagingTemplate;
import org.springframework.test.context.TestPropertySource;
import static org.junit.Assert.assertNotNull;
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest
#TestPropertySource(properties = {
"cloud.aws.region.static=us-east-1",
"cloud.aws.credentials.accessKey=zzzzz",
"cloud.aws.credentials.secretKey=zzzzzz",
"aws.sqs.endpoint = http://localhost:9324",
"aws.sqs.requestQueue = CreateSchemaRequest",
"aws.sqs.responseQueue = CreateSchemaResponse"
})
public class AWSSQSConfigTests {
#Mock
private AmazonSQSAsync amazonSqs;
#Mock
private ResourceIdResolver resourceIdResolver;
#Test
public void contextLoads() {
AWSSQSConfig config = new AWSSQSConfig();
QueueMessagingTemplate queueMessagingTemplate = config.queueMessagingTemplate(amazonSqs, resourceIdResolver);
assertNotNull("The response body must not be null", queueMessagingTemplate);
}
}
Is this a chicken and the egg situation, where the spring framework actually needs to run first to inject those config values? Do we need an integration test here instead?
EDIT with working solution...
Using the accepted answer, here is my working test! I was able to remove my dependency of the Spring framework.
#RunWith(MockitoJUnitRunner.class)
public class AWSSQSConfigTests {
#Mock
private AmazonSQSAsync amazonSqs;
#Mock
private ResourceIdResolver resourceIdResolver;
#InjectMocks
private AWSSQSConfig config;
#Before
public void setup() {
ReflectionTestUtils.setField(config, "AWSSqsEndpoint", "http://fake");
}
#Test
public void contextLoads() {
QueueMessagingTemplate queueMessagingTemplate = config.queueMessagingTemplate(amazonSqs, resourceIdResolver);
assertNotNull("The response body must not be null", queueMessagingTemplate);
}
}
Have you tried injecting mock to your class (or autowire it), and then setting that field it using ReflectionTestUtils? This is a nice test utils class that Spring provides that allows you to do something like what you want without doing code modifications.
I mean something like this:
#InjectMocks
private AWSSQSConfig awssqsConfig;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
ReflectionTestUtils.setField(awssqsConfig, "AWSSqsEndpoint", "putYourEndpointHere");
}
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)));
}