Send parameters to injected mock beans - spring

Doing a component test with testNG I need to mock a service that gets some external data.
To do this I configure the mock in the file spring-test-config.xml like this:
<beans xmlns=...>
<context:component-scan base-package="..." />
<bean id="dataRetrieverBean" class="org.my.project.DataRetrieverBeanMock" scope="prototype"/>
</beans>
And then I reference this config file in my test class like this:
#Test()
#ContextConfiguration(locations = { "classpath:spring-test-config.xml" })
public class MyNGTest extends AbstractTestNGSpringContextTests {
[...]
}
The mock always return "Hello world" when the component that I want to test calls "getStringResponse()" on it:
public class DataRetrieverBeanMock implements Response {
#Override
public String getStringResponse() {
return "Hello world";
}
}
Till here everything works as expected!
Now to test the component under different conditions I need that the String returned by DataRetrieverBeanMock (currently hardcoded with "Hello world") changes for each test case, for example the first test would test the component when the response is "success", the second when the response is "error 109" and so on.
How can I define the string to be returned inside the test case and pass it to the mock bean?

Related

How do I write a junit test for a simple controller method that returns a service method call?

I am new to Spring, and also Junit testing. I would like to test this method so it increases my test line coverage.
Here is the class:
#RestController
#RequestMapping("/")
public class SummaryController {
private final SummaryService summaryService;
#Autowired
public SummaryController(SummaryService summaryService) {
this.summaryService = summaryService;
}
#GetMapping("/summary")
#ResponseBody
public List<SummaryObject> getDirectoryList() {
return summaryService.getSummary();
}
}
So this is a springboot/react web. application. When we click the "summary" page, we load a table with my local file directory content. summaryService.getSummary() is the method that returns a populated object model which my controller then sends the same object back to the front-end.
Here is a test I wrote to try to test this method.
#WebMvcTest (TestController.class)
public class Test Controller {
List<SummaryObject> summaryObject;
#Mock
private SummaryService summaryService;
#Test
void testControllerMatch() {
this.summaryObject = new ArrayList<>();
this.summaryObject.add(new SummaryObjectModel("file name", "file size", "last modified"));
when(SummaryService.getSummary()).thenReturn(summaryObjectModel);
assertEquals(SummaryObjectModel, SummaryService.getSummary());
}
}
The test is passing, but I do not think it is testing what I want. I wanted it to test that the return statement returns the List of SummaryObjects.
I know the return statement is not tested because when I see coverage, the "return summaryService.getSummary();" line in my controller doesn't get tested (its red, not green. thus not tested).
How can I test that method indeed returns a List of SummaryObjects ?
What other kinds of tests can I write for such a simple method? How do I make that line covered in coverage ("green")?
Your controller code is not being covered because your test doesn't call that endpoint. To do that you might use mockMvc which apparently you were going to use. I suggest that you go through the example in the official doc.
Conceptually, your test will look like this:
#WebMvcTest
public class ControllerTest {
#MockBean
private SummaryService summaryService;
#Test
void testControllerMatch() {
List<SummaryObject> summaries = new SummaryObject("file name", "file size", "last modified");
when(summaryService.getSummary()).thenReturn(summaryObjectModel);
// the /summary endpoint gets called here and the controller code will finally be called
mockMvc.perform(get("/summary"))
.andExpect(status().isOk())
.andExpect(content().json("<JSON representation of the summary list >"));
}
}

Access CamelContext in Cucumber via Spring

I want to test my Apache Camel routes, which are wired together in Spring, with Cucumber.
My Test looks something like this:
#RunWith(SpringJUnit4ClassRunner.class)
#TestExecutionListeners([DependencyInjectionTestExecutionListener.class])
#ContextConfiguration(locations = ["/cucumber.xml"])
Class TestEnvironment {
public TestEnvironment(){
#Autowired
ProducerTemplate superTemplate
//more foo
}
World() {
new TestEnvironment()
}
Before() {}
Given(~/I want to send a Message "([^"]+)" to my "([^"]+)" Camel Route./) { String greatMessageString, String endpointURI ->
MockEndpoint fakeEndpoint = getMockEndpoint(endpointURI)
fakeEndpoint.expectedMessageCount(1)
superTemplate.sendBody(fakeEndpoint, greatMessageString) //always 'cannot invoke sendBody() on null object'
}
superTemplate never links to any of my Camel routes. So I tried to extend the TestEnvironment with CamelSpringTestSupport.
Class TestEnvironment extends CamelSpringTestSupport {
protected AbstactXmlApplicationContext createApplicationContext() {
return new ClassPathXmlApplicationContext(["/cucumber.xml"])
}
The template() method is normally available as ProducerTemplate when extending the class with CamelSpringTestSupport. But it does not pick up the CamelContext for my routes either. template is always null. So is the context for the TestEnvironment.
I feel like I'm pretty close already but lack a certain annotation or whatever... Would be thankful for new input.
Cheers

How to initialise/wire beans in Grails Spock unit tests?

I'm wanting to test a Grails controller that contains a bean (I'll move it to a Service when I get it working, but I just want to keep it simple now).
//resources.groovy
beans {
myBean(com.me.MyBean)
}
// MyBean.java
// this needs to be in java as it is playing with spring-data-neo4j
package com.me;
public class MyBean {
String show() {
return "Hello";
}
}
// TestController.groovy
package com.me
import com.me.MyBean
class TestController {
def myBean
def index() {
render myBean.show()
}
}
// TestControllerSpec.groovy
package com.me
import grails.test.mixin.TestFor
import spock.lang.Specification
import com.me.*
#TestFor(TestController)
class TestControllerSpec extends Specification {
def myBean
def setup() {
defineBeans {
myBean(com.me.MyBean) {bean->
bean.autowire = true
}
}
}
def cleanup() {
}
def "show() returns Hello"() {
when:
def rc = controller.myBean.show()
def rc2 = myBean.show()
then:
rc == "Hello"
rc2 == "Hello"
}
}
Within TestControllerSpec, myBean is null. controller.myBean is also null. I think this is because Spring is not picking the bean up and wiring it in. I gather that in unit tests not all spring beans are available, but what do I need to do to get controller.myBean to be instantiated and wired up correctly?
You must be mocking the myBean as below
def myBean = Mock(MyBean)
or
MyBean myBean = Mock()
and then stub out method for your need if required as below:
myBean.show >> "test data"
and then assign it to controller object which is already mocked for you.
controller.myBean = myBean
and there you go.
Or optionally you can stub out myBean and give stubbed implementations. For example,
MyBean myBean = Stub(){
show() >> {return "sample text"}
}
controller.myBean = myBean
The reason for doing this is we are not testing the integration of application entities like controller, views or domain but we are testing a single unit i.e. a method and hence we should be just testing it and for integration we should be using integration test cases which would be similar in everything except you won't require any mocking in normal scenarios.
Edit:
found another useful feature to mock services or beans using defineBeans closure as below:
defineBeans {
adapter(Adapter)
helperService(HelperService)
}
This will allow beans to be accessed from grailsApplication.
Hope it helps.

Does Spring #DirtiesContext reload Spring context?

I have a test class that looks like
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:/test-context.xml"})
#DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
public abstract class TestClass {
#Rule #Resource public JUnitRuleMockery jMockContext;
public void test1() {
//Expectations and test
}
public void test2() {
//Expectations and test
}
}
and in test-context.xml I define the JUnitRuleMockery plus several mock objects through a factory-method, like
<bean id="mockContextFactory" class="MockContextFactory" />
<bean id="jMockContext" factory-bean="mockContextFactory" factory-method="getContext" scope="prototype" />
<bean id="firstMock" factory-bean="mockContextFactory" factory-method="getFirstMock" />
<bean id="secondMock" factory-bean="mockContextFactory" factory-method="getSecondMock" />
MockContextFactory is
public class MockContextFactory
{
private JUnitRuleMockery jUnitRuleMockery;
public MockContextFactory() {
jUnitRuleMockery = new JUnitRuleMockery();
jUnitRuleMockery.setThreadingPolicy(new Synchroniser());
}
public JUnitRuleMockery getContext() {
return jUnitRuleMockery;
}
public FirstMock getFirstMock() {
return jUnitRuleMockery.mock(FirstMock.class);
}
//others getter
}
In TestClass I have several test methods and, due to the annotations #DirtiesContext, I am expecting the Spring context to be reloaded after each test execution (since each test sets expectations on mock objects, I have to reload Spring context every time). See #DirtiesContext from here. However, it appears that the Spring context is not reloaded: in fact, entering in debug mode at the beginning of test2 (supposedly test1 has been executed earlier) I can see jMockContext still holding expectations, execution list and errors (if any) from test1.
So, to end up with few questions, does #DirtiesContext really cause Spring context to be reloaded (as I understood from Spring Docs) or did I misunderstand the annotation? In the first case, what am I doing wrong? In the latter case, how can I force Spring context to be reloaded for every test?
EDIT, to delimit the problem: I had a code like the sample above running from few days, then today I created a new test in which I added an expectation which failed. Then, I saw all the other tests in the class failing for the same reason (while they where green until today). Debugging, I found out that jMockContext was never cleared, which means all tests were adding expectations to the same pool: of course, as long as no expectation failed, that was transparent and I did not notice it (test1 adds and passes few expectations, test2 takes a not-empty pool of expectations ALREADY passed and adds its own so there was no problem), but I do not like the previous situation and I would like to start each test with no previous set expectations at all.
Here is my solution. I do not know if this is limited to Spring 3.2 or if it is a general misunderstanding, but simply using #DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD) is not enough to cause the entire Spring context to be reloaded for each test. I had to add also the DirtiesContextTestExecutionListener in #TestExecutionListeners.
So, in the end, what worked for me was just to change the annotation of my TestClass to
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:/test-context.xml"})
#DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
#TestExecutionListeners({DirtiesContextTestExecutionListener.class})
public abstract class TestClass
Nothing else has to change.

Unit testing with Mockito

I am writing unit tests for service layer in my spring application.
Here is my service class
#Service
public class StubRequestService implements RequestService {
#Autowired
private RequestDao requestDao;
#Transactional(propagation = Propagation.REQUIRED, readOnly = true)
#Override
public Request getRequest(Long RequestId) {
Request dataRequest = requestDao.find(requestId);
return dataRequest;
}
}
Here is my test class
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration(locations = { "/META-INF/spring/applicationContext.xml" })
public class StubRequestServiceTest {
#Mock
public RequestDao requestDao;
StubRequestService stubRequestService; // How can we Autowire this ?
#org.junit.Before
public void init() {
stubRequestService = new StubRequestService(); // to avoid this
stubRequestService.setRequestDao(dataRequestDao);
// Is it necessary to explicitly set all autowired elements ?
// If I comment/remove above setter then I get nullPointerException
}
#Test
public void testGetRequest() {
Request request = new Request();
request.setPatientCnt("3");
when(requestDao.find(anyLong())).thenReturn(request);
assertEquals(stubRequestService.getRequest(1234L).getPatientCnt(),3);
}
}
Its working fine but I have few questions
How can we Autowire service class in test ? I am using constructor in init() method to create service object.
Do we have to set all Autowire element for service class ? For ex StubRequestService have autowired RequestDao which I need to set explicitly before calling test method otherwise it giveds nullPointerException as requestDao is null in StubRequestService.getRequest method.
Which are the good practices to follow while unit testing Spring service layer ? (If I am doing anything wrong).
Your test is fine. It doesn't even have to have the #ContextConfiguration annotation.
The whole point of dependency injection frameworks like Spring is to be able to unit test services by simply instantiating them, setting mock dependencies, and then call their methods.
You're doing it correctly. You don't need to have a Spring context for such unit tests. That's why they're called unit tests: they test it in isolation of all their actual dependencies, Spring included.
Side note: assuming you're using JUnit, the arguments of the assertXxx method should be swapped. The expected value comes before the actual value. It becomes important when the assertion fails and you have a message like "expecting 6 but was 3" rather than "expecting 3 but was 6".
If you really feel that it will make your tests easier to understand - you can initialize a spring context and fetch all of the objects from there. However, usually it will require creating a separate spring configuration XML file specifically for tests therefore I would not recommend it.
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("testApplicationContext.xml");
stubRequestService = (RequestService)applicationContext.getBean("myRequestServiceBean");
(and 3) Basically, I prefer testing each component of my application in total isolation from eachother and that's why I do not recommend what I described in [1].
What that means, is you take a separate logical slice of your application and test only it, while fully mocking up everything it tries to access.
Let's say you have three classes:
//Fetches stuff from some webservice and converts to your app domain POJOs
class DataAccessLayer {
public void setWebservice(Webservice ws) {...};
public MyObject getMyObject() {...};
}
//Formats the domain POJOs and sends them to some kind of outputstream or stuff.
class ViewLayer {
public void setOutputStream(OutputStream os) {...};
public void viewMyObject(MyObject mo) {...};
}
//Main entry point of our MyObject fetch-process-display workflow
class Controller {
public void setDataAccessLayer(DataAccessLayer dal) {...};
public void setViewLayer(ViewLayer vl) {...};
public void showMyObject() {
MyObject mo = dal.getMyObject();
...some processing here maybe...
vl.viewMyObject(mo);
}
}
Now, what tests can we write here?
Test if DataAccessLayer properly converts the object from mocked up WS to our domain object.
Test if ViewLayer properly formats the object given to him and writes it to mocked up output stream.
Test if Controller takes an object from mocked up DataAccessLayer processes it properly and sends it to mocked up ViewLayer.
Or You can use springockito
https://bitbucket.org/kubek2k/springockito/wiki/Home, it will make your tests cleaner

Resources