How to verify web responses when using Spring and test-mvc - spring

A question about how to use test-mvc for unit testing.
I have a simple controller:
#Controller
#RequestMapping("/users")
public class UserController {
private UserService business;
#Autowired
public UserController(UserService bus)
{
business = bus;
}
#RequestMapping(value="{id}", method = RequestMethod.GET)
public #ResponseBody User getUserById(#PathVariable String id) throws ItemNotFoundException{
return business.GetUserById(id);
}
(( My idea is to keep the controllers so thin as possible.))
To test this controller I am trying to do something like this.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:mvc-dispatcher-servlet.xml"})
public class UserControllerTest extends ControllerTestBase {
UserService mockedService;
#Before
public void Setup()
{
MockitoAnnotations.initMocks( this );
mockedService = mock(UserService.class);
}
#Test
public void ReturnUserById() throws Exception{
User user = new User();
user.setName("Lasse");
stub(mockedService.GetUserById("lasse")).toReturn(user);
MockMvcBuilders.standaloneSetup(new UserController(mockedService)).build()
.perform(get("/users/lasse"))
.andExpect(status().isOk())
.andExpect(?????????????????????????????);
}
My intention is to check that proper json code is returned,,,,,,
I am not a pro,,, so I have not found a way to replace ??????????????????????? with code to do verify the returned string but I am certain that there must be a elegant way to do this
Can anyone fill me in?
//lg

content().string(containsString("some part of the string"))
assuming that you have this import:
import static org.springframework.test.web.server.result.MockMvcResultMatchers.*;
Update: Adding jsonPath also based on your comments:
You can add a dependency to json-path, the 1.0.M1 seems to depend on an much older version of json-path though:
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>0.5.5</version>
<scope>test</scope>
</dependency>
With this your test can look like this:
.andExpect(jsonPath("$.persons[0].first").value("firstName"));

Related

Do java validation annotations run before AOP

I'm new in Spring Boot AOP.
Does an AOP method annotated with #Before run before java validation annotations (such as #NotNull)?
I have some other custom validations that need to run for every request but I need to run these validations after java validation annotations run.
Which one will run first?
my Controller:
#RestController
#RequestMapping("/users")
public class UserController {
private final UserService userService;
#Autowired
public UserController(UserService userService) {
this.userService = userService;
}
#PostMapping(value = "")
public List<User> getAllUsers(#Valid #RequestBody User user) {
return userService.getAllUsers();
}
}
and my advice:
#Aspect
#Component
public class AspectConfig {
#Pointcut(value = "within(#org.springframework.web.bind.annotation.RestController *)")
public void restControllers() {
}
#Before(value = "restControllers()")
public void logRequest(JoinPoint joinPoint) {
...
}
}
Does an AOP method annotated with #Before run before java validation annotations
No, it runs afterwards, just like you wish. See also this question. So you should be all set. Your logging advice should only be triggered if validation was successful, because only then the target method will be called.
You can implement a HandlerInterceptor if you wish to log/do something on the request level before validators kick in, see here.

WebMvcTest is too greedy

I want to write a WebMvcTest test for a single controller in my Spring Boot application. Among other things there are some custom Converters in my application. Although they are not needed for this particular controller that I want to test, Spring tries to create them anyway.
Now the problem: those custom converters require more beans from my application which are not initialised by WebMvcTest test slice. And don't want to mock tens of beans which are completely irrelevant for the particular test. Apart from specifying them all manually in excludeFilters, what are best practises for excluding some web components from specific WebMvcTest tests?
You could use a custom exclude filter in order to avoid loading converters into application context:
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = YourController.class, excludeFilters = #ComponentScan.Filter(type = CUSTOM, classes = NoConvertersFilter.class))
public class YourControllerTest {
...
}
class NoConvertersFilter extends TypeExcludeFilter {
private static final String CONVERTER_INTERFACE_NAME = Converter.class.getName();
#Override
public boolean match(#NonNull final MetadataReader metadataReader, #NonNull final MetadataReaderFactory metadataReaderFactory) throws IOException {
return Arrays.asList(metadataReader.getClassMetadata().getInterfaceNames()).contains(CONVERTER_INTERFACE_NAME);
}
}
With this approach you just have to add the excludeFilter to those controllers in which you don't want to have Converters loaded. No worries if a new converter is added: it'll be automatically excluded as far as it implements the converter interface.
For custom tests don't use WebMvcTest, create a custom configuration:
#SpringBootTest
#WebAppConfiguration
#RunWith(value = SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {SomeYourTestConfiguration.class})
public class TestClass {
private MockMvc mockMvc;
#Before
public void setup() {
var someController = new SomeController();
mockMvc = MockMvcBuilders.standaloneSetup(someController).addFilters(...)
.setMessageConverters(...).setControllerAdvice(...).setValidator(...);
}
#Test
public void test() {
//arrange
when(...).thenReturn(...);
//act
var response = mockMvc.perform(...).andReturn().getResponse();
//assert
...
}
}
You can configure your mockMvc how you want.

How to write unit test for my spring boot controller which uses a MongoRepository?

I managed to write a test for a basic controller which doesn't need any other services or APIs. But now I am struggling to apply this to a controller which interacts with a database.
I collected examples from different sources provided here on SO or on other sites google threw at me. Most of them are very old and are based on spring-boot 1.3 or 1.5 though I am using the latest 2.0.4.RELEASE
Some excerpts of what is working (I spare you the details as it is not relevant):
#RunWith(SpringRunner.class)
#WebMvcTest(HtmlController.class)
public class HtmlControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void testIndex() {
try {
mockMvc.perform(get("/"));//etc.
} catch (Exception e) {
fail();
}
}
}
My more complex controller #Autorwires this interface:
public interface SetRepository extends MongoRepository<SetEntity, String>
Here I found that I can just add #DataMongoTest to the test class and add a dependency for flapdoodle to my pom and it should work:
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
But I immediately get an InitializationError without any information about what is wrong. I found somewhere that I might need to add spring.data.mongodb.port=0 to the application.properties, but this didn't change a thing.
What am I missing? Does anyone have an example test that also uses the MongoRepository interface?
here you go with one example..
#DataMongoTest
#ExtendWith(SpringExtension.class)
public class MongoDbIntegrationTest {
#Autowired
private MongoTemplate mongoTemplate
#Test
public void test() {
//Create your document object
DBObject objectToSave = new DBObject();
objectToSave.set /// set properties..
mongoTemplate.save(objectToSave, "collection");
// then assert result..
assertThat(mongoTemplate.findAll(DBObject.class, "collection")).extracting("key")
.containsOnly("value");
}
}

Data is not saved when using remotely Neo4j Server with Spring Data Neo4j

I have created a Maven project using spring-data-neo4j. I have also installed the standalone Neo4j Server Community Edition 2.3.3. I am trying to save some Vertex objects to the database and then simply retrieve them to check everything works fine. Then, I would like to be able to open the created db in the standalone server for better visualization.
I am using as dependencies:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
The configuration class is:
#Configuration
#ComponentScan("neo4j.example")
#EnableAutoConfiguration
#EnableNeo4jRepositories("neo4j.example.repository")
public class App extends Neo4jConfiguration {
public App() {
System.setProperty("username", "neo4j");
System.setProperty("password", "root");
}
#Override
public SessionFactory getSessionFactory() {
return new SessionFactory("neo4j.example.model");
}
#Override
#Bean
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public Session getSession() throws Exception {
return super.getSession();
}
#Override
public Neo4jServer neo4jServer() {
return new RemoteServer("http://localhost:7474");
}
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
My NodeEntity looks like:
#NodeEntity
public class Vertex {
private String name;
#GraphId
private Long id;
#Relationship(type = "PAIRS_WITH", direction = "UNDIRECTED")
public Set<Vertex> teammates;
public Vertex() { }
// getters, setters, equals, toString
}
The repository:
#Repository
public interface VertexRepository extends GraphRepository<Vertex> {
Vertex findByName(String name);
List<Vertex> findByTeammatesName(String name);
}
The service:
#Service
public class VertexServiceImpl implements VertexService {
#Autowired
private VertexRepository vertexRepository;
#Override
#Transactional
public Vertex create(Vertex vertex) {
return vertexRepository.save(vertex);
}
#Override
#Transactional
public Iterable<Vertex> findAll() {
return vertexRepository.findAll();
}
//....
}
Then I have a controller with two simple methods to save a vertex and then query the database.
#RestController
#RequestMapping("/api/")
public class GraphController {
#Autowired
VertexService vertexService;
#RequestMapping(value = "addvertex", method = RequestMethod.GET)
public void add() {
Vertex v = new Vertex();
v.setId(1l);
v.setName("name");
Vertex v2 = new Vertex();
v2.setId(2l);
v.worksWith(v2);
vertexService.create(v);
}
#RequestMapping(value = "all", method = RequestMethod.GET)
public Iterable<Vertex> getAll() {
return vertexService.findAll();
}
}
When I save the vertex to the db there is no error. When I call /all the db is empty. I checked messages.log and there is no exception...last lines being:
2016-03-26 14:25:15.716+0000 INFO [o.n.k.i.DiagnosticsManager] Interface Microsoft Wi-Fi Direct Virtual Adapter-WFP 802.3 MAC Layer LightWeight Filter-0000:
2016-03-26 14:25:15.716+0000 INFO [o.n.k.i.DiagnosticsManager] --- INITIALIZED diagnostics END ---
2016-03-26 14:25:15.747+0000 INFO [o.n.k.i.DiagnosticsManager] --- STOPPING diagnostics START ---
2016-03-26 14:25:15.747+0000 INFO [o.n.k.i.DiagnosticsManager] --- STOPPING diagnostics END ---
2016-03-26 14:25:15.747+0000 INFO [o.n.k.i.f.GraphDatabaseFacade] Shutdown started
Any help is fully appreciated!
I managed to solve my problem. The configuration is fine, the problem was I was trying to set the id property of a #NodeEntity object. Even if I remove the #GraphId property the vertex is not saved. This post addresses the same problem.
In Good Relations:The Spring Data Neo4j Guide Book it is mentioned that: "If the field is simply named 'id' then it is not necessary to annotate it with #GraphId as the OGM will use it automatically."
It would be nice if there was some kind of warning/ error message or more explicitly mentioned in the documentation that you cannot setId() and that the node will not be saved in the db if you do. Hope this will save somebody some time!
You are mixing embedded and remote server?
You should look for your data in the remote server.
Also you must have disabled auth for this to work in the server, or you have to provide username (neo4j) and password to your config.
DO NOT START AN EMBEDDED DATABASE ON THE SAME DIRECTORY AS THE SERVER USES

Mocking a service within service (JUnit)

I have the following service:
#Service
public class PlayerValidationService {
#Autowire
private EmailService emailService;
public boolean validatePlayerEmail(Player player) {
return this.emailService.validateEmail(player.getEmail());
}
Now in my junit test class i'm using a different 3rd service that uses PlayerValidationService :
public class junit {
#autowire PlayerAccountService playerAccountService ;
#Test
public test() {
this.playerAccountService .createAccount();
assertAllSortsOfThings();
}
Is it possible to mock the EmailService within the PlayerAccountService when using annotation based autowiring? (for example make the mock not checking the validation of the email via the regular email provider we work with)
thanks.
There are a couple of ways in which you could do this. First the simplest option is to ensure that your service provides a setEmailService(EmailService) method. In which case you just replace the Spring-injected implementation with your own.
#Autowired
private PlayerValidationService playerValidationService;
#Mock
private EmailService emailService;
#Before
public void setup() {
initMocks(this);
playerValidationService.setEmailService(emailService);
}
A shortcoming of that approach is that an instance of the full-blown EmailService is likely to be created by Spring. Assuming that you don't want that to happen, you can use 'profiles'.
In your test packages, create a configuration class which is only active in a particular profile:
#Configuration
#Profile("mockemail")
public class MockEmailConfig {
#Bean(name = "emailService")
public EmailService emailService() {
return new MyDummyEmailService();
}
}
And add an annotation to your test to activate that profile:
#ActiveProfiles({ "mockemail" })
public class PlayerValidationServiceTest {
//...
}

Resources