I've my main class something like this:
class MyClass{
String bar(String inputString){
String url = "https:x.y.z/p/q"; //the URL is framed dynamically based on other class attributes
final String payloadInJson = getPayload(inputString)
final String response = doPostRequest(url, payloadInJson)
}
private static String doPostRequest(final String url, final String postData) throws IOException {
final RequestBody body = RequestBody.create(JSON, postData)
final Request request = new Request.Builder()
.url(url)
.post(body)
.build()
final Response response = createOkHttpClient().newCall(request).execute()
if (!response.isSuccessful()) {
throw new RuntimeException("...")
}
response.networkResponse().header("Location")
}
private static OkHttpClient createOkHttpClient() {
Config config = new ConfigBuilder()
.withTrustCerts(true)
.build()
def httpClient = HttpClientUtils.createHttpClient(config)
httpClient = httpClient.newBuilder().authenticator(Authenticator.NONE).build()
httpClient
}
}
and My Consumer test case is:
#SpringBootTest(classes = Application.class)
#AutoConfigureStubRunner(stubsMode = StubRunnerProperties.StubsMode.LOCAL, ids = ["com.ex:foobar:+:stubs:8090"])
class MyClassTest{
#Inject
private MyClass myClass
def 'happyPath'(){
given:
...
when:
String res = myClass.bar('lorem...')
}
}
Question is how to mock the OkHttp URL and use localhost?
Or is it that in the test case, I can refer the actual URL framed?
If you use Spring Cloud Contract, we start an HTTP server on a given or random port. It's enough for you to set you OK Http Client to point to the started server. Example
PSEUDOCODE:
class MyClass{
private String url = "https:x.y.z/p/q";
String bar(String inputString){
final String payloadInJson = getPayload(inputString)
final String response = doPostRequest(this.url, payloadInJson)
}
// package scope
void setUrl(String url) {
this.url = url;
}
}
and in your test you can then set the stub's port and url
THE TEST (PSEUDOCODE AGAIN):
#SpringBootTest(classes = Application.class)
#AutoConfigureStubRunner(stubsMode = StubRunnerProperties.StubsMode.LOCAL, ids = ["com.ex:foobar"])
class MyClassTest{
#Inject
private MyClass myClass
#StubRunnerPort("foobar") int stubPort;
def 'happyPath'(){
given:
myClass.url = "http://localhost:${stubPort}"
when:
String res = myClass.bar('lorem...')
}
}
a better option is to use a proper #Configuration class where you define beans and inject the URL via constructor. Anyways, hopefully, it shows you how you can approach the problem.
Related
I am trying to init object with default if not initialized by the user.
I want the user be able not to add params in the request but I will have it with the default value.
I'm not sure what I am missing but I get Null Pointer exception.
Having this object
#Builder
#Data
#Valid
#RequiredArgsConstructor
#AllArgsConstructor
#NoArgsConstructor(force = true, access = AccessLevel.PRIVATE)
public class Request {
private final List<Author> authors;
private final Config config;
private Params params;
}
#Builder
#AllArgsConstructor
#NoArgsConstructor(force = true, access = AccessLevel.PRIVATE)
#Data
#Setter(AccessLevel.NONE)
public class Params {
#Builder.Default private boolean adultOnly = false;
}
fun main(args: Array<String>) {
val request = Request(
authors,
config(
key,
level
)
)
also using builder throw null exception
val request = QueriesRequest.builder()
.userQueries(userQueries)
.configurationKey(
QueryConfigurationKey(configurationKey,
granularityLevel))
.site(site).build()
request.params.adultOnly // throw NULL , I expected to have the default false.
}
What I am missing?
The params field in the Request class is never initialized and remains null. You could set a default value for it:
#Builder
#Data
#Valid
#RequiredArgsConstructor
#AllArgsConstructor
#NoArgsConstructor(force = true, access = AccessLevel.PRIVATE)
public class Request {
private final List<Author> authors;
private final Config config;
#Builder.Default
private Params params = Params.builder().build();
}
I'm using the Params' builder itself to initialize the default value, so that you will get the default values as defined by that builder as well.
I'm testing a post method in my controller that only return a String and using Mockito to mock the service call. My problem is that when the service method is called on controller it return null.
#RunWith(SpringRunner.class)
#WebMvcTest(ProcessGroupController.class)
public class ProcessGroupRestControllerTest {
.............
#Test
public void givenAllNifiArguments_whenImportProcessGroup_thenReturnJsonOk() throws Exception {
NiFiArguments niFiArguments = NiFiArguments.builder()......flowVersion("3").build();
String expected = "1689d61b-624d-4574-823d-f1b4755882e1";
String json = mapper.writeValueAsString(niFiArguments);
//Mock service call
when(nifiService.importProcessGroup(niFiArguments)).thenReturn(expected);
mvc.perform(post("/nifi/pg-import").contentType(MediaType.APPLICATION_JSON).content(json))
.andExpect(status().isCreated())......);
}
The controller:
#PostMapping("/pg-import")
public ResponseEntity<String> importProcessGroup(#RequestBody NiFiArguments niFiArguments)
throws NiFiClientException {
log.info("Called method importFlow");
String result = nifiService.importProcessGroup(niFiArguments);
return new ResponseEntity<String>(result, HttpStatus.CREATED);
}
String result = null
I have similar tests that return a POJO and it works perfectly
As ekalin said my builder class needed to implement equals and hashcode:
#Builder
#Getter
#EqualsAndHashCode
public class NiFiArguments {
private String bucketIdentifier;
private String flowIdentifier;
private String flowVersion;
private String baseUrl;
}
I try to mock a controller which contains a util method inside even though I mock the util method, the mvcMock ignore the result from the when(...) and call the method again with empty parameters which lead to nullpointerexception
How I can ship the call of
when(utilMock.getOperatorsAdNameWrapper(userName, adNames)).thenReturn(roleSet);
with the mockMvc.perform?
#GetMapping(value = {"/wellbore"})
public String wellboreForm(Model model, #RequestParam("mode") String mode, HttpServletRequest request) {
Set<String> operators = new LinkedHashSet<>();
String userName = (String) request.getSession().getAttribute("userName");
Set<String> operatorsSet = (HashSet<String>) request.getSession().getAttribute("userRoles");
Set<String> operatorsAdName = util.getOperatorsAdNameWrapper(userName, operatorsSet);
operatorsAdName.forEach(adName -> {
Query query = new Query()
.setClassname(Wellbore.CLASS)
.eq(Wellbore.operatorsGroup, adName);
operators.addAll(getWellboresNameList(query));
});
model.addAttribute("wellboreDataList", operators);
model.addAttribute("wellboreData", new WellboreForm());
return "ui/selectWellbore";
}
public static Set<String> getOperatorsAdName(String userName, Set<String> operatorsAdName) {
operatorsAdName.removeIf(x -> x.equals(userName)
|| x.equals("SCOUT")
|| x.equals("GTO")
|| x.equals("KADME")
|| x.equals("offline_access")
|| x.equals("uma_authorization"));
return operatorsAdName;
}
public Set<String> getOperatorsAdNameWrapper(String userName, Set<String> operatorsAdName) {
return getOperatorsAdName(userName,operatorsAdName);
}
#Mock
private Util utilMock;
#Test
#DisplayName("GET /wellbore - Select Wellbore")
void testMockMvc() throws Exception {
HttpServletRequest req = Mockito.mock(HttpServletRequest.class);
when(req.getAttribute("userName")).thenReturn("abcd");
String userName = (String) req.getAttribute("userName");
//Here I get the correct result Result
when(utilMock.getOperatorsAdNameWrapper(userName, adNames)).thenReturn(roleSet);
//another call made here with empy parameters to utilMock.getOperatorsAdNameWrapper("", null)
mockMvc.perform(get("/wellbore").param("mode","selectWellbore")
.sessionAttr("wellboreDataList", new LinkedHashSet<>())
.sessionAttr("wellboreData", new WellboreForm())
)
.andExpect(status().isOk())
.andExpect(view().name("ui/selectWellbore"))
.andExpect(model().attribute("wellboreDataList", hasSize(2)));
}
1) In the Controller move the line:
util.getOperatorsAdNameWrapper(userName, operatorsSet);
into a package level method:
Set<String> getOperatorsAdNameWrapper(userName, operatorsSet){
return util.getOperatorsAdNameWrapper(userName, operatorsSet);
}
2) In your test use SpyBean:
#SpyBean
private Controller controllerSpy;
#Test
#DisplayName("GET /wellbore - Select Wellbore")
void testMockMvc() throws Exception {
doReturn(roleSet).when(controllerSpy).getOperatorsAdNameWrapper(userName, adNames);
The general gist is that you cannot mock a static call with vanilla Mockito. You have to refactor a bit first.
The Problem was with the Util class
since I am using mockmvc as unit testing, not as integration test by standaloneSetup
mockMvc = MockMvcBuilders
//To avoid loading springContext
.standaloneSetup(controller)
.setViewResolvers(viewResolver())
.build();
so the Util class not loaded to the context to solve this you have to option
Move the wrapper method in the util class to the service class and from there you can wrapper the static method in the Util class
Add the util class to the controller constructor
I have a method like below in my class that I'm trying to test:
class SomeHelper {
ByteArrayOutputStream fooBar (Request request) {
ByteArrayOutputStream baos = someParser.parseData(getRequestFileInputStream(request.filename))
return baos
}
InputStream getRequestFileInputStream(String filename) {
//return intputStream of object from S3
}
....
}
In the above, getRequestFileInputStream is a method that takes as parameter a name of the file. It fetches the inputstream of that file from AWS S3. While testing fooBar method from Spock, I would like to provide a mock for the getRequestFileInputStream method because I don't want to use the implementation of this method that is in the class since it goes to another bucket name.
Is it possible to do this?
Below is what I've tried:
class SomeHelperSpec extends Specification{
//this is the implementation of getRequestFileInputStream I want to use while testing
InputStream getObjectFromS3(String objectName) {
def env = System.getenv()
AwsClientBuilder.EndpointConfiguration endpoint = new AwsClientBuilder.EndpointConfiguration(env["endpoint_url"], env["region_name"])
AmazonS3ClientBuilder builder = AmazonS3ClientBuilder.standard()
builder.setEndpointConfiguration(endpoint)
builder.setCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(env["ACCESS_KEY"], env["SECRET_KEY"])))
AmazonS3 s3 = builder.build()
return s3.getObject("testbucket", objectName).getObjectContent()
}
def "test fooBar" () {
given:
someHelper = new SomeHelper()
someHelper.getRequestFileInputStream(_) >> getObjectFromS3(fileName)
someHelper.someParser = Mock(SomeParser) {
....
}
Request requestInstance = new Request()
request.filename = fileName
request.fileType = fileType
expect:
someHelper.fooBar(requestInstance).getText == returnVal
where:
fileType | fileName | returnVal
"PDF" | "somepdf.pdf" | "somereturnval"
}
}
However, the above doesn't work because it is still trying to call the original implementation of getRequestFileInputStream in SomeHelper instead of using the mocked implementation provided in the spec.
You can use a spy
def someHelper = Spy(SomeHelper)
someHelper.getRequestFileInputStream(_) >> getObjectFromS3(fileName)
See Spies.
You can use a real object but with overridden method:
given:
someHelper = new SomeHelper() {
#Override
InputStream getRequestFileInputStream(String filename) {
return getObjectFromS3(fileName)
}
}
Please take a look at the test class below. I am trying to do an LDAP search with Spring LDAP Template. I am able to search and produce a list of entries corresponding to the search criteria without the Spring LDAP template by using the DirContext as shown in the method searchWithoutTemplate(). But when I use a LdapTemplate, I end up with a NPE as shown further below. I am sure I must be missing something. Can someone help please?
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.LdapName;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.DefaultDirObjectFactory;
import org.springframework.ldap.core.support.LdapContextSource;
public class LDAPSearchTest {
//bind params
static String url="ldap://<IP>:<PORT>";
static String userName="cn=Directory Manager";
static String password="password123";
static String bindDN="dc=XXX,dc=com";
//search params
static String base = "ou=StandardUser,ou=XXXCustomers,ou=People,dc=XXX,dc=com";
static String filter = "(objectClass=*)";
static String[] attributeFilter = { "cn", "uid" };
static SearchControls sc = new SearchControls();
public static void main(String[] args) throws Exception {
// sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
sc.setReturningAttributes(attributeFilter);
searchWithTemplate(); //NPE
//searchWithoutTemplate(); //works fine
}
public static void searchWithTemplate() throws Exception {
DefaultDirObjectFactory factory = new DefaultDirObjectFactory();
LdapContextSource cs = new LdapContextSource();
cs.setUrl(url);
cs.setUserDn(userName);
cs.setPassword(password);
cs.setBase(bindDN);
cs.setDirObjectFactory(factory.getClass ());
LdapTemplate template = new LdapTemplate(cs);
template.afterPropertiesSet();
System.out.println((template.search(new LdapName(base), filter, sc,
new AttributesMapper() {
public Object mapFromAttributes(Attributes attrs)
throws NamingException {
System.out.println(attrs);
return attrs.get("uid").get();
}
})));
}
public static void searchWithoutTemplate() throws NamingException{
Hashtable env = new Hashtable(11);
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, url);
//env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, userName);
env.put(Context.SECURITY_CREDENTIALS, password);
DirContext dctx = new InitialDirContext(env);
NamingEnumeration results = dctx.search(base, filter, sc);
while (results.hasMore()) {
SearchResult sr = (SearchResult) results.next();
Attributes attrs = sr.getAttributes();
System.out.println(attrs);
Attribute attr = attrs.get("uid");
}
dctx.close();
}
}
Exception is:
Exception in thread "main" java.lang.NullPointerException
at org.springframework.ldap.core.support.AbstractContextSource.getReadOnlyContext(AbstractContextSource.java:125)
at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:287)
at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:237)
at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:588)
at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:546)
at LDAPSearchTest.searchWithTemplate(LDAPSearchTest.java:47)
at LDAPSearchTest.main(LDAPSearchTest.java:33)
I am using Spring 2.5.6 and Spring LDAP 1.3.0
A quick scan showed that it's the authenticationSource field of AbstractContextSource that is the culprit. That file includes the following comment on the afterPropertiesSet() method:
/**
* Checks that all necessary data is set and that there is no compatibility
* issues, after which the instance is initialized. Note that you need to
* call this method explicitly after setting all desired properties if using
* the class outside of a Spring Context.
*/
public void afterPropertiesSet() throws Exception {
...
}
That method then goes on to create an appropriate authenticationSource if you haven't provided one.
As your test code above is most definitely not running within a Spring context, and you haven't explicitly set an authenticationSource, I think you need to edit your code as follows:
...
cs.setDirObjectFactory(factory.getClass ());
// Allow Spring to configure the Context Source:
cs.afterPropertiesSet();
LdapTemplate template = new LdapTemplate(cs);