I'm trying to write a controller unit test for a #PostMapping but am getting a failed test
Status expected:<201> but was:<400>
The controller works as expected in Postman so I know it actually works, but it would be nice to have a working unit test as well.
What am I doing wrong?
TEST
#Test
#DisplayName("CREATE NEW ENFORCEMENT ACTION")
void testCreateNewEnforcementAction() throws Exception {
EnforcementAction mockAction = new EnforcementAction();
mockAction.setSystemId(1289);
mockAction.setCurrentStaff("ralbritton");
mockAction.setCurrentStatus("NEEDED");
mockAction.setCreatedOn(LocalDateTime.now());
mockAction.setCreatedBy("ralbritton");
mockAction.setEaType("IF");
mockAction.setEaCode("CAP");
mockAction.setDeleted(false);
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(mockAction);
mockMvc.perform(MockMvcRequestBuilders.post("/api/enforcementactions/action")
.contentType(MediaType.APPLICATION_JSON)
.content(json)
.characterEncoding("utf-8"))
.andExpect(status().isCreated()); //Have also tried this as .isOK() (didn't make a diff)
//.andReturn(); ///Added and removed this line to see if it made a differnce (it did not)
}
CONTROLLER BEING TESTED
#PostMapping("/api/enforcementactions/action")
public ResponseEntity<?> createNewEnforcementAction(#RequestBody EnforcementAction newAction) {
service.createEnforcementAction(newAction);
return new ResponseEntity<>(newAction, HttpStatus.CREATED);
}
MODEL
UPDATE: I'm adding in the model to show that there is not Bean Validation on fields
public class EnforcementAction {
private Integer eaId;
private Integer systemId;
private String alternateNumber;
private String systemName;
private Integer tenschdId;
private String currentStaff;
private String currentStatus;
private LocalDate dateActionIssued;
private LocalDate dateActionClosed;
private boolean deleted;
private LocalDateTime createdOn;
private String createdBy;
private LocalDateTime modifiedOn;
private String lastModifiedBy;
private String eaType;
private String eaCode;
private String comment;
private Long daysSinceCreate;
private List<EaStaffHistory> staffAssigned = new ArrayList<>();
private List<EaDocStatusHistory> documentStatus = new ArrayList<>();
private List<EaComments> eaComments = new ArrayList<>();
/** Constructors */
public EnforcementAction() {
}
public EnforcementAction(Integer eaId, Integer systemId, String systemName, Integer tenschdId,
String currentStaff, String currentStatus, Long daysSinceCreate,
String createdBy, String lastModifiedBy, LocalDate dateActionIssued, LocalDate dateActionClosed,
String eaType, String eaCode, LocalDateTime createdOn) {
this.eaId = eaId;
this.systemId = systemId;
this.tenschdId = tenschdId;
this.systemName = systemName;
this.currentStaff = currentStaff;
this.currentStatus = currentStatus;
this.createdBy = createdBy;
this.lastModifiedBy = lastModifiedBy;
this.dateActionClosed = dateActionClosed;
this.dateActionIssued = dateActionIssued;
this.eaType = eaType;
this.eaCode = eaCode;
this.daysSinceCreate = daysSinceCreate;
this.createdOn = createdOn;
}
...getters and setters....
POSTMAN showing successful post:
EDIT: I've updated the OP code to reflect current state. Still having the same issue though.
The reason for the 400 is how you send your payload to your controller. You are not serializing the Java object to JSON, but use the .toString() representation of it:
.content(String.valueOf(mockAction)))
Either make use of the ObjectMapper or prepare a custom JSON string:
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(mockAction);
mockMvc.perform(MockMvcRequestBuilders
.post("/api/enforcementactions/action")
.contentType(MediaType.APPLICATION_JSON)
.content(json))
.andExpect(status().isCreated());
OK so I finally figured out my problem and I'm posting it here in case someone else has the same issue. While #Rieckpil was correct in all his suggestions (and I will mark his answer as correct) the other problem I was having was in my mockAction object. I had:
mockAction.setCreatedOn(LocalDateTime.now())
Even though createdOn is of type LocalDateTime it was getting deconstructed in the body to look like this:
"createdOn": {
"dayOfWeek": "WEDNESDAY",
"dayOfYear": 204,
"month": "JULY",
"year": 2020,
"dayOfMonth": 22,
"hour": 12,
"minute": 43,
"monthValue": 7,
"nano": 839000000,
"second": 10,
"chronology": {
"id": "ISO",
"calendarType": "iso8601"
}
}
When I passed this as the createdOn variable into Postman I was able to get a meaningful error .HttpMessageNotReadableException: JSON parse error: Expected array or string.; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Expected array or string. at [Source: (PushbackInputStream); line: 12, column: 21] (through reference chain: gov.deq.utah.enforcementactions.models.enforcementActions.EnforcementAction["createdOn"])
The test passed when I removed this. I kept all other suggestions provided.
Related
I dont know why but Im always getting NullPointer and no idea why and how exactly this test should looks like. Its about method: webServiceTemplate():
#Configuration
public class ErdConfiguration {
#Autowired
private EJwtProperties eJwtProperties;
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
// this package must match the package in the <generatePackage> specified in pom.xml
marshaller.setContextPath("erdUserRoles.wsdl");
return marshaller;
}
public WebServiceTemplate webServiceTemplate() {
WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
webServiceTemplate.setMarshaller(marshaller());
webServiceTemplate.setUnmarshaller(marshaller());
webServiceTemplate.setDefaultUri(eJwtProperties.getRoles().getErdServiceUri());
return webServiceTemplate;
}
}
and EJwtProperties class which it uses:
public class EJwtProperties {
private Map<String, String> claims = new HashMap<>();
private String signingKey;
private SourceTokenConfig sourceToken = new SourceTokenConfig();
private RolesConfig roles = new RolesConfig();
private List<String> generateEjwtRoles = Collections.emptyList();
private boolean cacheDisabled = false;
#Data
public static class SourceTokenConfig {
private boolean embedSourceToken = false;
private String embeddedTokenClaimName = "source-token";
}
#Data
public static class RolesConfig {
private boolean rolesEnabled = false;
private String rolesClaimName = "roles";
private String erdAppId;
private String erdServiceUri;
}
}
My code so far looks like this and got null pointer while Im trying to check getRoles() in when-thenReturn :
#Mock
private EJwtProperties eJwtProperties;
#InjectMocks
private ErdConfiguration underTest;
Jaxb2Marshaller marshaller;
#BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}
#Test
void webServiceTemplateTest() {
EJwtProperties.RolesConfig roles = new EJwtProperties.RolesConfig();
roles.setErdServiceUri("testErdServiceUri");
eJwtProperties.setRoles(roles);
underTest = new ErdConfiguration();
when(eJwtProperties.getRoles()).thenReturn(roles); //this one passed
when(eJwtProperties.getRoles().getErdServiceUri()).thenReturn(roles.getErdServiceUri()); //here nullPointer
// underTest.webServiceTemplate(); //this is what I was planning to do next
//assertEquals(underTest.webServiceTemplate(), eJwtProperties.getRoles().getErdServiceUri()); //or this
// assertEquals(marshaller, underTest.webServiceTemplate().getMarshaller());
// assertEquals(marshaller, underTest.webServiceTemplate().getUnmarshaller());
}
}
Please keep in mind that I'm still learning tests. Id be thankful for any help. How the hack it should looks like? What am I missing that it return null ? Should I initialize whole properties??
Regarding a code example like this one:
var someProperty: String = "defaultValue"
get() = field
set(value) { field = value }
Is it mandatory to write the get-/set-methods right below the attribute-declaration? It won't work if I have something in between attribute-declaration and get/set-definition?
Is the nesting just for better reading or is it part of the syntax?
because you're using var and its public all this part
get() = field
set(value) { field = value }
is redundant and the Kotlin compiler should alert about it.
also, you can see that when you compile your code to kotlin byte code with or without the setter and getter
the outcome is the same
#Metadata(
mv = {1, 4, 1},
bv = {1, 0, 3},
k = 1,
d1 = {"\u0000\u0014\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u000e\n\u0002\b\u0005\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002R\u001a\u0010\u0003\u001a\u00020\u0004X\u0086\u000e¢\u0006\u000e\n\u0000\u001a\u0004\b\u0005\u0010\u0006\"\u0004\b\u0007\u0010\b¨\u0006\t"},
d2 = {"Lorg/example/Fo;", "", "()V", "someProperty", "", "getSomeProperty", "()Ljava/lang/String;", "setSomeProperty", "(Ljava/lang/String;)V", "stream"}
)
public final class Fo {
#NotNull
private String someProperty = "defaultValue";
#NotNull
public final String getSomeProperty() {
return this.someProperty;
}
public final void setSomeProperty(#NotNull String var1) {
Intrinsics.checkNotNullParameter(var1, "<set-?>");
this.someProperty = var1;
}
}
and last for the order question, The full syntax for declaring a property is
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
so you cant add anything in between the declaration parts
I am using the Jackson library to convert Java objects to YAML format. Based on the documentation I found on the Internet, I was able to quickly write a function that does the conversion.
I am seeking to convert the following classes to YAML:
public class RequestInfo
{
private String thePath;
private String theMethod;
private String theURL;
private List<ParamInfo> theParams = new ArrayList<>();
// getters and setters
}
public class ParamInfo
{
private String paramName;
private String paramType;
// getters and setters
}
Using Jackson's ObjectMapper, I can easily generate the YAML:
public String basicTest()
{
ObjectMapper theMapper = new ObjectMapper(new YAMLFactory());
RequestInfo info = new RequestInfo();
info.setThePath("/");
info.setTheMethod("GET");
info.setTheURL("http://localhost:8080/");
List<ParamInfo> params = new ArrayList<>();
params.add(new ParamInfo("resource","path"));
info.setTheParams(params);
String ret = null;
try
{
ret = theMapper.writeValueAsString(info);
}
catch(Exception exe)
{
logger.error(exe.getMessage());
}
return(ret);
}
The YAML I get is below:
---
thePath: "/"
theMethod: "GET"
theURL: "http://localhost:8080/"
theParams:
- paramName: "resource"
paramType: "path"
The YAML I get is OK, but it has some problems in my eyes. One probem is the "---" that it begins with. Another is the fact that I would like to be able to group the information in a manner similar to the YAML below:
RequestInfo:
thePath: "/"
theMethod: "GET"
theURL: "http://localhost:8080/"
theParams:
- paramName: "resource"
paramType: "path"
All of the examples I am seeing on the Internet use an Employee class, and talk about how to convert that class to YAML, but do not tell how to avoid the "---" (or change it into soething more descriptive). I also cannot find anything that tells how to group the YAML in the manner I describe.
Does anyone know how to do this? Is there a way to eliminate the "---", or create a name (like "RequestInfo") that groups together the translated data in an object?
You can ignore --- by disable YAMLGenerator.Feature.WRITE_DOC_START_MARKER..
If you want to wrap value under class name then u need to use #JsonRootName...
Try with this:
RequestInof class:
#JsonRootName("RequestInfo")
public class RequestInfo
{
private String thePath;
private String theMethod;
private String theURL;
private List<ParamInfo> theParams = new ArrayList<>();
// getters and setters
}
Test:
public String basicTest()
{
ObjectMapper theMapper = new ObjectMapper(new YAMLFactory().disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER));
theMapper.enable(SerializationFeature.WRAP_ROOT_VALUE); RequestInfo info = new RequestInfo();
info.setThePath("/");
info.setTheMethod("GET");
info.setTheURL("http://localhost:8080/");
List<ParamInfo> params = new ArrayList<>();
params.add(new ParamInfo("resource","path"));
info.setTheParams(params);
String ret = null;
try
{
ret = theMapper.writeValueAsString(info);
}
catch(Exception exe)
{
logger.error(exe.getMessage());
}
return(ret);
}
Jackson version: 2.9.8
I'm converting an object not to JSON, but to a Map, using objectMapper.convertValue(myObject, Map.class)
Here is a simple example to reproduce. My Pojo:
public static class Foo {
private final String string;
private final LocalDate date;
public Foo(String string, LocalDate date) {
this.string = string;
this.date = date;
}
public String getString() {
return string;
}
public LocalDate getDate() {
return date;
}
}
Conversion code:
public static void main(String[] args) {
Foo foo = new Foo("hello", LocalDate.of(1999, 12, 31));
ObjectMapper objectMapper = new ObjectMapper();
Map map = objectMapper.convertValue(foo, Map.class);
Object string = map.get("string");
System.out.println("string: >>>"+ string +"<<< of type "+ string.getClass().getName());
Object date = map.get("date");
System.out.println("date: >>>"+ date +"<<< of type "+ date.getClass().getName());
}
This prints:
string: >>>hello<<< of type java.lang.String
date: >>>{year=1999, month=DECEMBER, chronology={id=ISO, calendarType=iso8601}, era=CE, dayOfMonth=31, dayOfWeek=FRIDAY, dayOfYear=365, leapYear=false, monthValue=12}<<< of type java.util.LinkedHashMap
When enabling the JavaTimeModule with
objectMapper.registerModule(new JavaTimeModule());
it prints:
string: >>>hello<<< of type java.lang.String
date: >>>[1999, 12, 31]<<< of type java.util.ArrayList
But what I need is to KEEP the LocalDate instance in the Map. No conversion. I can't find how to configure Jackson to keep (not convert) a type.
I have tried with a noop converter:
private static class KeepLocalDateJsonSerializer extends JsonSerializer<LocalDate> {
#Override
public void serialize(LocalDate localDate, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeObject(localDate);
}
};
registering it with
SimpleModule mod = new SimpleModule("KeepLocalDate");
mod.addSerializer(LocalDate.class, new KeepLocalDateJsonSerializer());
objectMapper.registerModule(mod);
and this leads to a StackOverflowError with detail:
ReadOnlyClassToSerializerMap.typedValueSerializer(ReadOnlyClassToSerializerMap.java:85)
JsonMappingException: Infinite recursion $Foo["date"]
Apparently Jackson tries to convert the returned value also. Looks like a bug to me. If the converter writes the same object through, even the same instance, then obviously the intention is to keep it. When converting to JSON then I understand we can't keep a LocalDate instance, but in a Map that's not the case.
How can I achieve this, in a generic way, so that it works for any data structure? Without annotations in the Pojo. So that all instances of a certain data type are passed through untouched.
There is this similar question Jackson Convert Object to Map preserving Date type but it's 5 years old, uses Date not LocalDate, and the solution with SerializationFeature.WRITE_DATES_AS_TIMESTAMPS does not work here.
I try to use foreign collections in ORMLite. However, I dont know how to convert it into list. I try to do something like this :
public class Car implements Serializable {
#DatabaseField(columnName = "carId" , generatedId = true, id=true)
private int id;
#DatabaseField(columnName = "carNumber")
private String mNumber;
#DatabaseField(columnName = "carName")
private String mName;
#ForeignCollectionField(eager = true,columnName = "carParts")
private Collection<Part> mParts;
ArrayList<Part> parts = new ArrayList<>(mParts);
public ArrayList<Part> getParts() {
return parts;
}
public void setParts(ArrayList<Part> parts) {
this.parts = parts;
}
but when I try to use it I get exception :
java.lang.NullPointerException: collection == null
at this line :
ArrayList<Part> parts = new ArrayList<>(mParts);
please, help.
The reason is simple - you have to wait until mParts will be initialized by ORMLite library, then you can create ArrayList from it.
public ArrayList<Part> getParts() {
return new ArrayList<>( mParts );
}