How to mock Spring dependencies with JUnit 5? - java

We have to test class, see below. Our question is how can we fill the dependencies, so that we can test the original class you see below.
public class FragenAntwortenDataprovider extends SortableDataProvider<FragenAntworten, String> {
#SpringBean
private IFragenAntwortenService service;
private IModel<FragenAntworten> filter;
public FragenAntwortenDataprovider(IModel<FragenAntworten> filter){
this.filter = filter;
Injector.get().inject(this);
setSort("id", SortOrder.DESCENDING); // absteigend sortieren
}
#Override
public Iterator<? extends FragenAntworten> iterator(long first, long count){
List<FragenAntworten> list = load();
List<FragenAntworten> sublist = list.subList((int) first, (int) (first+count));
return sublist.iterator();
}
#Override
public long size() {return getListSize();}
#Override
public IModel<FragenAntworten> model(FragenAntworten object) {
return Model.of(object);
}
private void sort(final List<FragenAntworten> list){
}
private long getListSize(){
List<FragenAntworten> list = service.ladeAlle(filter.getObject().getSystem());
return list.size();
}
private List<FragenAntworten> load(){
List<FragenAntworten> list = service.ladeAlle(filter.getObject().getSystem());
return list;
}
}

Since you use Spring you can use its ReflectionUtils helper class to inject the dependency:
IFragenAntwortenService service = mock(IFragenAntwortenService.class);
IModel<FragenAntworten> model = ...;
FragenAntwortenDataprovider provider = new FragenAntwortenDataprovider(model);
Field serviceField = ReflectionUtils.findField("service", provider);
ReflectionUtils.setField(serviceField, provider, service);
Another option is to introduce package-private setter for service field and avoid using reflection.

You should have your original class have the dependencies injected with #Autowired or #Inject. Actually you should not use field injection, but constructor injection. Then you should use the #ExtendWith(MockitoExtension.class) for the JUnit 5 test class. Every dependency you have to define as a #Mock and for the class you wish to test you use the #InjectMocks annotation.
This is the constructor injection to use:
public class FragenAntwortenDataprovider extends SortableDataProvider<FragenAntworten, String> {
private IFragenAntwortenService service;
private IModel<FragenAntworten> filter;
#Inject
public FragenAntwortenDataprovider(IFragenAntwortenService service, IModel<FragenAntworten> filter){
this.service = service;
this.filter = filter;
//I guess you don't need the injector anymore
//Injector.get().inject(this);
setSort("id", SortOrder.DESCENDING); // absteigend sortieren
}
//Rest of class
}
And this is then a test-class:
#ExtendWith(MockitoExtension.class)
public class testClass() {
#Mock
private IFragenAntwortenService service;
#Mock
private IModel<FragenAntworten> filter;
#InjectMocks
FragenAntwortenDataprovider sut; //System under Test
#Test
void test() {
//Testcode
// Control the mock with
when(service.anymethod()).thenReturn(result);
var result = sut.callMethodToTest();
// verify all calls
verify(service, times(1)).anymethod();
// and assert
assertNotNull(result);
}
}

Related

#Autowired not working in multi-module application

I need to #Autowire database services or repositores form "database" module in "game" module.
Already added those annotations in main "Application" class:
#Configuration
#EnableMethodSecurity
#SpringBootApplication
#EnableAutoConfiguration
#EntityScan(basePackages="com.rydzwr.tictactoe")
#ComponentScan(basePackages="com.rydzwr.tictactoe")
#EnableJpaRepositories(basePackages="com.rydzwr.tictactoe")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Adding them in constructor is much more difficult, becouse I'm need to create list of instances of those classes in StrategySelector:
#Service
public class GameStrategySelector {
private final List<BuildGameStrategy> strategyList = asList(
new LocalPlayerGameStrategy(),
new MultiPlayerGameStrategy()
);
public BuildGameStrategy chooseStrategy(GameDto gameDto) {
return strategyList
.stream()
.filter(strategy -> strategy.applies(gameDto))
.findFirst()
.orElse(new ErrorGameTypeStrategy());
}
}
Or, maybe someone has better idea, for StrategySelector logic?
Here is class with given problem:
#Component
public class LocalPlayerGameStrategy implements BuildGameStrategy {
#Autowired
private GameService gameService;
#Autowired
private UserService userService;
#Autowired
private PlayerService playerService;
#Override
#Transactional
public void buildGame(GameDto gameDto) {
Game game = new GameBuilder(gameDto.getGameSize(), gameDto.getGameDifficulty()).build();
gameService.save(game);
User caller = userService.findByName(SecurityContextHolder.getContext().getAuthentication().getName());
assert caller != null;
for (PlayerDto playerDto : gameDto.getPlayers()) {
Player player = new PlayerBuilder().setGame(game).setUser(caller).setPlayerDetails(playerDto).build();
playerService.save(player);
}
game.setState(GameState.IN_PROGRESS);
gameService.save(game);
}
#Override
public boolean applies(GameDto gameDto) {
return gameDto.getPlayers().stream().allMatch(p -> p.getPlayerType().equals(PlayerType.LOCAL.name()));
}
}
I tried to autowire repositores and services with implemented logic as well.
Every time all of them:
#Autowired
private GameService gameService;
#Autowired
private UserService userService;
#Autowired
private PlayerService playerService;
ARE NULL
I tried everything I found on google
You can't create new instances of LocalPlayerGameStrategy and MultiPlayerGameStrategy inside GameStrategySelector they won't be spring beans so they can't #Autowire other beans.
Modify your GameStrategySelector to inject all BuildGameStrategy instead of creating them manually
#Service
public class GameStrategySelector {
private final List<BuildGameStrategy> strategyList;
public GameStrategySelector(List<BuildGameStrategy> allStrategies){
strategyList = allStrategies;
}
public BuildGameStrategy chooseStrategy(GameDto gameDto) {
return strategyList
.stream()
.filter(strategy -> strategy.applies(gameDto))
.findFirst()
.orElse(new ErrorGameTypeStrategy());
}
}

SpringBoot Mockito: when..thenReturn giving an exception

So, currently, I'm testing on a Service class
This is my ConvertService.java
#Service
public class ConvertService {
private final NetworkClient networkClient; //NetworkClient is a Service too
private final ConvertUtility convertUtility;
public ConvertService(Network networkClient) {
convertUtility = ConvertFactory.of("dev", "F");
this.networkClient = networkClient
}
public Response convert(Request request) {
User user = networkClient.getData(request.getId()); //User is POJO class
Context context = convertUtility.transform(request.getToken()) //getToken returns a String
//Context is a normal Java
}
}
This is my ConvertServiceTest.java
#SpringBootTest
#RunWith(MockitoJunitRunner.class)
class ConvertServiceTest {
#MockBean
private NetworkClient networkClient;
#Mock
ConvertUtility convertUtility;
private ConvertService convertService;
#BeforeEach
void setUp() {
convertService = new ConvertService(networkClient);
}
private mockMethod() {
Request request = Request(1000);
User user = new User("user1");
Context context = new Context();
when(networkClient.getData(anyLong())).thenReturn(user);
when(convertUtility.transform(any(String.class)).thenReturn(context);
Response response = convertService.convert(request); //it throws me an exception here
}
}
convertService.convert(request); throws an exception
pointing inside convertUtility.transform(request.getToken())
I'm not sure why it's processing everything from transform method, when I wrote
when(convertUtility.transform(any(String.class)).thenReturn(context);
Can anyone please help?
EDIT: ConvertUtility is a read-only library
Inside your public constructor, you're using a static factory method to get an instance of the ConvertUtility. You'd have to mock the static ConvertUtility.of() method to work with a mock during your test.
While Mockito is able to mock static methods, I'd recommend refactoring (if possible) your class design and accepting an instance of ConvertUtility as part of the public constructor:
#Service
public class ConvertService {
private final NetworkClient networkClient; //NetworkClient is a Service too
private final ConvertUtility convertUtility;
public ConvertService(Network networkClient, ConvertUtility convertUtility) {
this.convertUtility = convertUtility
this.networkClient = networkClient
}
}
With this change, you can easily mock the collaborators of your ConvertService when writing unit tests:
#ExtendWith(MockitoExtension.class)
class ConvertServiceTest {
#Mock
private NetworkClient networkClient;
#Mock
private ConvertUtility convertUtility;
#InjectMocks
private ConvertService convertService;
#Test // make sure it's from org.junit.jupiter.api
void yourTest() {
}
}

Mock method return type in java

Below is main code consist of one util class and service class using it
#PropertySource("classpath:atlas-application.properties")
public class ApacheAtlasUtils {
#Value("${atlas.rest.address}")
private String atlasURL;
#Value("${atlas.rest.user}")
private String atlasUsername;
#Value("${atlas.rest.password}")
private String atlasPassword;
private AtlasClientV2 client;
public AtlasClientV2 createClient() {
if (client == null) {
return new AtlasClientV2(new String[] {atlasURL}, new String[] {atlasUsername, atlasPassword});
} else {
return client;
}
}
}
Service Class is below :-
#Override
public Page<SearchResultDto> findFilesWithPages(QueryParent queryParent, Pageable pageable)
throws AtlasServiceException {
// Some code
client = new ApacheAtlasUtils().createClient();
//some code
}
I am writing unit test for service method and I am getting exception for createClient method asking for values for url, username and password which should not happen as this should be mocked but the mocking is giving me below error
java.lang.IllegalArgumentException: Base URL cannot be null or empty.
at com.google.common.base.Preconditions.checkArgument(Preconditions.java:141)
at org.apache.atlas.AtlasServerEnsemble.<init>(AtlasServerEnsemble.java:35)
at org.apache.atlas.AtlasBaseClient.determineActiveServiceURL(AtlasBaseClient.java:318)
at org.apache.atlas.AtlasBaseClient.initializeState(AtlasBaseClient.java:460)
at org.apache.atlas.AtlasBaseClient.initializeState(AtlasBaseClient.java:448)
at org.apache.atlas.AtlasBaseClient.<init>(AtlasBaseClient.java:132)
at org.apache.atlas.AtlasClientV2.<init>(AtlasClientV2.java:82)
at com.jlr.stratus.commons.utils.ApacheAtlasUtils.createClient(ApacheAtlasUtils.java:40)
at com.jlr.stratus.rest.service.impl.FileSearchService.findFilesWithPages(FileSearchService.java:49)
The Test code is as follows:-
private FileSearchService fileSearchService;
#Spy
private ApacheAtlasUtils apacheAtlasUtils;
#Mock
private AtlasClientV2 client;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
fileSearchService = new FileSearchService();
}
#Test
public void findFilesWithPages_searchAll() throws AtlasServiceException {
Mockito.doReturn(client).when(apacheAtlasUtils).createClient();
service.search(queryParent,pageable);
}
Your idea with spying is adequate (you can even go for mocking if you do not actually need any true implementation of that class).
The problem lies in the implementation:
// Some code
client = new ApacheAtlasUtils().createClient();
//some code
}
Instead of having the ApacheAtlasUtils as an instance variable (or a supplier method) you create the instance on the fly.
Mockito is not smart enough to catch that operation and replace the real object with you spy.
With the supplier method you can set up your test as follows:
#Spy
private FileSearchService fileSearchService = new FileSearchService();
#Spy
private ApacheAtlasUtils apacheAtlasUtils = new ApacheAtlasUtils();
#Mock
private AtlasClientV2 client;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
doReturn(apacheAtlasUtils).when(fileSearchService).getApacheUtils();
}
in your SUT:
#Override
public Page<SearchResultDto> findFilesWithPages(QueryParent queryParent, Pageable pageable)
throws AtlasServiceException {
// Some code
client = getApacheUtils().createClient();
//some code
}
ApacheAtlasUtils getApacheUtils(){
return new ApacheAtlasUtils();
}

Testing a controller with an auto wired component is null when calling the controller from a test case

I have a controller
#RestController
public class Create {
#Autowired
private ComponentThatDoesSomething something;
#RequestMapping("/greeting")
public String call() {
something.updateCounter();
return "Hello World " + something.getCounter();
}
}
I have a component for that controller
#Component
public class ComponentThatDoesSomething {
private int counter = 0;
public void updateCounter () {
counter++;
}
public int getCounter() {
return counter;
}
}
I also have a test for my controller.
#RunWith(SpringRunner.class)
#SpringBootTest
public class ForumsApplicationTests {
#Test
public void contextLoads() {
Create subject = new Create();
subject.call();
subject.call();
assertEquals(subject.call(), "Hello World 2");
}
}
The test fails when the controller calls something.updateCounter(). I get a NullPointerException. While I understand it's possible to add #Autowired to a constructor I would like to know if there is anyway to do this with an #Autowired field. How do I make sure the #Autowired field annotation works in my test?
Spring doesn't auto wire your component cause you instantiate your Controller with new not with Spring, so Component is not instatntiated
The SpringMockMvc test check it correct:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class CreateTest {
#Autowired
private WebApplicationContext context;
private MockMvc mvc;
#Before
public void setup() {
mvc = MockMvcBuilders
.webAppContextSetup(context)
.build();
}
#Test
public void testCall() throws Exception {
//increment first time
this.mvc.perform(get("/greeting"))
.andExpect(status().isOk());
//increment secont time and get response to check
String contentAsString = this.mvc.perform(get("/greeting"))
.andExpect(status().isOk()).andReturn()
.getResponse().getContentAsString();
assertEquals("Hello World 2", contentAsString);
}
}
The #Autowired class can be easily mocked and tested with MockitoJUnitRunner with the correct annotations.
With this you can do whatever you need to do with the mock object for the unit test.
Here is a quick example that will test the Create method call with mocked data from ComponentThatDoesSomething.
#RunWith(MockitoJUnitRunner.class)
public class CreateTest {
#InjectMocks
Create create;
#Mock
ComponentThatDoesSomething componentThatDoesSomething;
#Test
public void testCallWithCounterOf4() {
when(componentThatDoesSomething.getCounter()).thenReturn(4);
String result = create.call();
assertEquals("Hello World 4", result);
}
}
Use Mockito and inject a mock that you create. I would prefer constructor injection:
#RestController
public class Create {
private ComponentThatDoesSomething something;
#Autowired
public Create(ComponentThatDoesSomething c) {
this.something = c;
}
}
Don't use Spring in your Junit tests.
public CreateTest {
private Create create;
#Before
public void setUp() {
ComponentThatDoesSomething c = Mockito.mock(ComponentThatDoesSomething .class);
this.create = new Create(c);
}
}

Jersey Test #Autowired field in tested class is null

I have a little problem. I think this is typical question. However, I can't find good example. My application is using Jersey. And I want to test controller by client as test. Controller has private field - StudentService. When I debug test I see, that field is null. This leads to error. And I need to inject this field. I tried this:
My Controller
#Path("/student")
#Component
public class StudentResourse {
#Autowired
private StrudentService service; // this field Spring does not set
#Path("/getStudent/{id}")
#GET
#Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public Student getStudent(#PathParam("id") long id) {
return service.get(id);
}
}
My JUnit test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "classpath:config.xml")
#TestExecutionListeners({ DbUnitTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class })
public class StudentResourseTest extends JerseyTest {
private static final String PACKAGE_NAME = "com.example.servlet";
private static final String FILE_DATASET = "/data.xml";
#Autowired
private StudentService service; // this field is setted by Spring, but I do not need this field for test
public StudentResourseTest() {
super(new WebAppDescriptor.Builder(PACKAGE_NAME).build());
}
#Override
protected TestContainerFactory getTestContainerFactory() {
return new HTTPContainerFactory();
}
#Override
protected AppDescriptor configure() {
return new WebAppDescriptor.Builder("restful.server.resource")
.contextParam("contextConfigLocation",
"classpath:/config.xml").contextPath("/")
.servletClass(SpringServlet.class)
.contextListenerClass(ContextLoaderListener.class)
.requestListenerClass(RequestContextListener.class).build();
}
#Test
#DatabaseSetup(FILE_DATASET)
public void test() throws UnsupportedEncodingException {
ClientResponse response = resource().path("student").path("getStudent")
.path("100500").accept(MediaType.APPLICATION_XML)
.get(ClientResponse.class);
Student student = (Student) response.getEntity(Student.class);
} }
I guees, that problem is in test class. Because, when I run my application not in test, I can directly request students and everything working fine. But when I test classes, internal field of Controller does not setted. How to fix this bug? Thanks for your answers.
This is in my config.xml
<context:component-scan base-package="com.example" />
<bean id="StudentResourse" class="com.example.servlet.StudentResourse">
<property name="service" ref="studentService" />
</bean>
<bean id="service" class="com.example.service.StudentServiceImpl" />
One issue may be that you're trying to configure your test application in constructor and in configure() method. Use one or another but not both because in this case your configure() method is not invoked and hence you may not be using SpringServlet and everything that is defined in this method.
Reference: https://github.com/jiunjiunma/spring-jersey-test and http://geek.riffpie.com/unit-testing-restful-jersey-services-glued-together-with-spring/
Idea is to get a hold of the application context inside jersey by using ApplicationContextAware interface. There after we can grab the exact bean already created by spring, in your case, StudentService. Below example shows a mocked version of the dependency, SampleService, used to test the resource layer apis.
Resource class delegating the processing to a service layer
#Component
#Path("/sample")
public class SampleResource {
#Autowired
private SampleService sampleService;
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path ("/{id}")
public Sample getSample(#PathParam("id") int id) {
Sample sample = sampleService.getSample(id);
if (sample == null) {
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
return sample;
}
}
Service layer encapsulating business logic
#Service
public class SampleService {
private static final Map<Integer, Sample> samples = new HashMap<>();
static {
samples.put(1, new Sample(1, "sample1"));
samples.put(2, new Sample(2, "sample2"));
}
public Sample getSample(int id) {
return samples.get(id);
}
}
Unit test for the above resource
public class SampleResourceTest extends SpringContextAwareJerseyTest {
private SampleService mockSampleService;
// create mock object for our test
#Bean
static public SampleService sampleService() {
return Mockito.mock(SampleService.class);
}
/**
* Create our own resource here so only the test resource is loaded. If
* we use #ComponentScan, the whole package will be scanned and more
* resources may be loaded (which is usually NOT what we want in a test).
*/
#Bean
static public SampleResource sampleResource() {
return new SampleResource();
}
// get the mock objects from the internal servlet context, because
// the app context may get recreated for each test so we have to set
// it before each run
#Before
public void setupMocks() {
mockSampleService = getContext().getBean(SampleService.class);
}
#Test
public void testMock() {
Assert.assertNotNull(mockSampleService);
}
#Test
public void testGetSample() {
// see how the mock object hijack the sample service, now id 3 is valid
Sample sample3 = new Sample(3, "sample3");
Mockito.when(mockSampleService.getSample(3)).thenReturn(sample3);
expect().statusCode(200).get(SERVLET_PATH + "/sample/3");
String jsonStr = get(SERVLET_PATH + "/sample/3").asString();
Assert.assertNotNull(jsonStr);
}
}
SpringContextAwareJerseyTest
#Configuration
public class SpringContextAwareJerseyTest extends JerseyTest {
protected static String SERVLET_PATH = "/api";
final private static ThreadLocal<ApplicationContext> context =
new ThreadLocal<>();
protected String getResourceLocation() {
return "example.rest";
}
protected String getContextConfigLocation() {
return getClass().getName();
}
static private String getContextHolderConfigLocation() {
return SpringContextAwareJerseyTest.class.getName();
}
protected WebAppDescriptor configure() {
String contextConfigLocation = getContextConfigLocation() + " " +
getContextHolderConfigLocation();
Map<String, String> initParams = new HashMap<>();
initParams.put("com.sun.jersey.config.property.packages",
getResourceLocation());
initParams.put("com.sun.jersey.api.json.POJOMappingFeature", "true");
return new WebAppDescriptor.Builder(initParams)
.servletClass(SpringServlet.class)
.contextParam(
"contextClass",
"org.springframework.web.context.support.AnnotationConfigWebApplicationContext")
.contextParam("contextConfigLocation", contextConfigLocation)
.servletPath(SERVLET_PATH) // if not specified, it set to root resource
.contextListenerClass(ContextLoaderListener.class)
.requestListenerClass(RequestContextListener.class)
.build();
}
protected final ApplicationContext getContext() {
return context.get();
}
#Bean
public static ContextHolder contextHolder() {
return new ContextHolder();
}
private static class ContextHolder implements ApplicationContextAware {
#Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
context.set(applicationContext);
}
}
}
Using the above with jersey 1.8

Categories