I've created an expected exception attribute by implementing the NUnit.Framework.IExpectException interface, as documented there (http://www.nunit.org/index.php?p=exception&r=2.6.2 ) and it works pretty well. It validates that the thrown exception is of the right type and validates the value of some properties on the exception.
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class ExpectedHttpErrorExceptionAttribute : ExpectedExceptionAttribute, IExpectException
{
public HttpStatusCode StatusCode { get; set; }
public ExpectedHttpErrorExceptionAttribute()
:base(typeof(HttpError))
{}
public ExpectedHttpErrorExceptionAttribute(HttpStatusCode statusCode)
:this()
{
StatusCode = statusCode;
}
public void HandleException(Exception ex)
{
Assert.That(ex, Is.TypeOf(ExpectedException), "Expected exception of type '{0}' but an exception of type '{1}' has been throwned.", typeof(HttpError).FullName, ex.GetType().FullName);
var httpStatusCode = ((HttpError) ex).StatusCode;
Assert.That(httpStatusCode, Is.EqualTo(StatusCode), "Expected status code '{0}' but was '{1}'.", StatusCode, httpStatusCode);
}
}
My problem is that I want to unit test that attribute, but I can't figure a way to test the following case :
A unit test that have the attribute should fail if no exception are thrown.
I just can't figure a way to write a unit test for that case, since the HandleException is not called when no exception are thrown.
Any suggestion?
The reason you are failing to write a test here is that you are trying to test functionality which is out of responsibility of your class.
Your class validates the exception only in case if exception is thrown. When there is no exception thrown there is nothing to validate :)
It is responsibility of test runner (NUnit or some another) to fail the test when test method is decorated with ExpectedExceptionAttribute (or inherited attribute) and there is no exception thrown during test.
So, you don't need to write a test fot such a scenario.
Related
How do I configure my spring boot service so that errors such as 500 don't potentially leak implementation details such as stacktraces.
{
"timestamp": "2019/05/01 15:06:17",
"status": 500,
"error": "Internal Server Error",
"message": "Type definition error: [simple type, class net.i2p.crypto.eddsa.math.ed25519.Ed25519LittleEndianEncoding]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class net.i2p.crypto.eddsa.math.ed25519.Ed25519LittleEndianEncoding and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.Collections$UnmodifiableRandomAccessList[0]->........)",
"path": "/api/test"
}
Note: here the stacktrace is in the message and not the exception part of the json.
As you can see I am already formatting the timestamp with:
#Component
public class CustomErrorAttributes extends DefaultErrorAttributes {
private static final DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
private static final String TIMESTAMP = "timestamp";
#Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
//Let Spring handle the error first
Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, includeStackTrace);
//Format & update timestamp
Object timestamp = errorAttributes.get(TIMESTAMP);
if(timestamp == null) {
errorAttributes.put(TIMESTAMP, dateFormat.format(new Date()));
} else {
errorAttributes.put(TIMESTAMP, dateFormat.format((Date)timestamp));
}
return errorAttributes;
}
}
But I need to handle the message too.
If this 500 was the only error I could just do:
errorAttributes.put("message", "Server error. Contact support.");
However, all the errors go through here and that would override all the messages.
I could check if the status is 500 and only modify it then. However, there are other errors that can be generated that also might leak stacktraces.
Using #RestControllerAdvice seems to require knowing every exception that is generated and having an #ExceptionHandler for each and knowing which status code to respond with.
Is there a cleaner way to handle this?
It may not be the "cleanest" approach, but with projects I've been on we had a "standard format" for our Error Responses across projects, so we had a custom object with the fields that matched our orgs standard (HttpStatus, Reason, ect.) that extended RuntimeException. Then in our controllers, services, repos, ect we would catch exceptions and create this object accordingly and throw the custom one up instead. Based upon where it happened in the app (repo, service, controller, ect.) we could give our own custom verbage to it, but still log out the full exception in our server logs so we could investigate later
For example if we caught an error in our repository we would create our custom error object, set the Reason to DB unavailable (really all the consumer needs to know), set the status to HttpStatus.SERVICE_UNAVAILABLE (we tracked these with reasons and httpstatus with enums to keep status the same across modules), and throw the custom object up to the controller to be returned.
Sorry if this was a longwinded answer that may not give you what you want, I'm not too familiar with how you're trying to do it so figured I'd just give an example of other methods. I'll put some sample code as well
Custom Exception:
data class MyException(
val reason: String,
val httpStatus: HttpStatus? = null
) : RuntimeException(reason)
Method for creation:
fun createApiException(errorCode: ErrorCodeEnum) = MyException(
reason = errorCode.reason,
httpStatus = errorCode.httpStatus,
)
Spring-boot provides us with a standard method to handle exceptions using spring aop concept. You can use the #ControllerAdvice and #Exceptionhandled annotations to handle exceptions from a spring-boot rest endpoint so that a custom exception is always thrown from a rest endpoint with proper error code and error response.
The #ResponseStatus() annotation can be used to customize the response code being thrown.
For example consider the custom exception :
#ResponseStatus(HttpStatus.NOT_FOUND)
public class DataNotFoundException extends RuntimeException {
public DataNotFoundException(String exception) {
super(exception);
}
}
We can throw this error from a rest GET mapping when a data is not found like :
#GetMapping("/trains/{id}")
public Resource<Student> retrieveTrains(#PathVariable long id) {
Optional<Trains> trains = trainRepository.findById(id);
if (!train.isPresent())
throw new DataNotFoundException("id-" + id);
Resource<Trains> resource = new Resource<Trains>(train.get());
ControllerLinkBuilder linkTo = linkTo(methodOn(this.getClass()).retrieveAllTrains());
resource.add(linkTo.withRel("all-trains"));
return resource;
}
Default error response provided by Spring Boot contains all the details that are typically needed.
However, you might want to create a framework independent response structure for your organization. In that case, you can define a specific error response structure.
For example :
public class ErrorDetails {
private Date timestamp;
private String message;
private String details;
public ErrorDetails(Date timestamp, String message, String details) {
super();
this.timestamp = timestamp;
this.message = message;
this.details = details;
}
To use this error node we use :
#ControllerAdvice
public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(DataNotFoundException.class)
public final ResponseEntity<ErrorDetails> handleUserNotFoundException(DataNotFoundException ex, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(),
request.getDescription(false));
return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
}
#ExceptionHandler(DataNotFoundException.class) indicates that this
method would handle exceptions of the specific type.
new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND) - Create an
error response object and return it with a specific Http Status.
For a more generalized exception handler you can define a method that handles exception of the type Exception.class, that way you don't have to know every exception.
Like :
#ExceptionHandler(Exception.class)
public final ResponseEntity<ErrorDetails> handleAllExceptions(Exception ex, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(),
request.getDescription(false));
return new ResponseEntity<>(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR);
}
Reference from : https://www.javaguides.net/2019/02/spring-boot-2-angular-7-crud-example-tutorial.html
This is a demo controller.
#PostMapping("/rest/new")
public ResponseEntity<MessageDto> newUser(#RequestBody UserDto userDto) {
return ResponseEntity.ok(new MessageDto().setMessage(userService.createUser(userDto)));
}
This is service layer.
#Override
public String createUser(UserDto userDto) {
// Do Something
return "Successful!!";
}
This is the test code to test the controller
#Test
public void testPostRestController() throws Exception {
UserDto userDto = new UserDto();
userDto.setName("AA");
userDto.setEmail("a#a.a");
userDto.setId((long) 1);
when(userService.createUser(userDto)).thenReturn("Successful!!");
mockMvc.perform(post("/rest/new")
.contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)
.content(new ObjectMapper().writeValueAsString(userDto)))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(jsonPath("$.message", is(userService.createUser(userDto))))
.andDo(MockMvcResultHandlers.print());
}
The problem is when I run the test code, it is supposed to check the response status and response message. The response status matched but the problem is the response message always returns as null.
error:
java.lang.AssertionError: JSON path "$.message"
Expected: is "Successful!!"
but: was null
Am I missing something here?
Try using
when(userService.createUser(any(UserDto.class))).thenReturn("Successful!!");
Instead of
when(userService.createUser(userDto)).thenReturn("Successful!!");
This should ideally fix the NPE
The following document details exception handling in remoting for Service Fabric (I use V2):
https://learn.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-services-communication-remoting#remoting-exception-handling
It has the following paragraph:
All remote exceptions thrown by the service API are sent back to the
client as AggregateException. RemoteExceptions should be DataContract
serializable; if they are not, the proxy API throws ServiceException
with the serialization error in it.
I have made the following exception, in a .NET Core class library shared between the services:
[DataContract]
public class DuplicateEntityException : Exception
{
public DuplicateEntityException(string message = "Duplicate entity.") : base(message) { }
}
I get the following message after throwing the exception in the called service:
System.AggregateException: One or more errors occurred. (The exception DuplicateEntityException was unhandled on the service and could not be serialized for transferring to the client.
If I just throw Exception it serializes correctly.
Any help in making my exception class DataContract serializable would be much appreciated.
All I had to do was:
[Serializable()]
public class DuplicateEntityException : Exception, ISerializable
{
public DuplicateEntityException(string message = "Duplicate entity.") : base(message) { }
public DuplicateEntityException(SerializationInfo info, StreamingContext context) : base(info, context) { }
}
Consider the following code in a model. the function deleteUser(NULL) will trigger an exception.
class Model_UserModel extends Zend_Db_Table_Abstract{
protected $_name = 'users';
protected $_primary = 'id';
public function deleteUser($id){
$row=$this->find($id)->current();
if($row){
$row->delete();
return true;
}else{
throw new Zend_Exception("Delete function failed; could not find row!");
}
}
}
I use PHPUnit to test this code and I want to check that the exception is indeed triggered when NULL is passed to the function deleteUser. The code in the testing class goes as follows:
class Application_Model_UserModelTest extends PHPUnit_Framework_TestCase{
...
//deleting a user with ID=NULL should fail
public function testDeleteNull(){
$e = null;
try{
$this->_users->deleteUser(NULL);
}catch (Exception $e) {}
if($e) {
$this->assertTrue(TRUE);
}else{
$this->assertTrue(FALSE);;
}
}
While this seems to work, I am wondering if there is a better way to do this. I have reviewed questions:
PHPUnit assert that an exception was thrown?
Problem testing exceptions with PHPUnit and Zend Framework
But I did not fully understand them / see how that applies in this case (testing a model, not a controller).
Any better way to test the exception is thrown?
Any advice will be much appreciated.
A problem with the way you do it is that it accepts any exception since all exceptions inherit from Exception. So you may miss a bug because the exception thrown was not the exception you expected.
Use annotations.
/**
* #expectedException Zend_Exception
*/
public function testDeleteNull(){
$this->_users->deleteUser(NULL);
}
You might want to create custom exception class to be more accurate though. Some people will argue that using exceptedException you can't assert the exception message and they are right. Simply there isn't universal and 'correct' solution for it.
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
StatusCodeException Vs. RuntimeException in GWT
I want to trigger RPC callback "onFailure" if the session expires in the server.
I created a custom RPC AsyncCallback that handles the "session expired" event from the server.
I overrode RemoteServiceServlet to validate the session prior to invoking the method. So basically, it is not the declared method that throws the exception but the custom RemoteServiceServlet. It still goes to the "onFailure" in the client async but the Throwable object is still of type "StatusCodeException" without the EXPIRED_SESSION_MSG message. Any ideas?
Custom RemoteServiceServlet:
public class XRemoteServiceServlet extends RemoteServiceServlet {
private final static String EXPIRED_SESSION_MSG = "ERROR: Application has expired session.";
#Override
protected void onAfterRequestDeserialized(RPCRequest rpcRequest) {
HttpServletRequest httpServletRequest = this.getThreadLocalRequest();
HttpSession session = httpServletRequest.getSession(false);
if (session != null) {
final String sessionIdFromRequestHeader = getSessionIdFromHeader();
if (!isNullOrEmptyString(sessionIdFromRequestHeader)) {
final String sessionId = session.getId();
if (!sessionId.equals(sessionIdFromRequestHeader)) {
throw new RuntimeException(EXPIRED_SESSION_MSG);
}
}
Custom AsyncCallback:
public class XAsyncCallback<T> implements AsyncCallback<T> {
private final static String EXPIRED_SESSION_MSG = "ERROR: Application has expired session.";
#Override
public void onFailure(Throwable caught) {
final String message = caught.getMessage();
if (!isNullOrEmptyString(message) && message.contains(EXPIRED_SESSION_MSG)) {
com.google.gwt.user.client.Window.Location.reload();
}
}
#Override
public void onSuccess(T arg0) {
}
/**
* Returns true if the string is null or equals to the empty string.
*
* #param string the string to test
* #return true if the string is empty
*/
private static boolean isNullOrEmptyString(String string) {
return string == null || "".equals(string);
}
}
See here for handling exceptions with GWT RPC.
An "expected failure" is an exception thrown by a service method that is declared in the signature of the service method. These exceptions are serialized back to the client.
"Unexpected expections" are errors that are not part of the service method's signature, or that result from SecurityExceptions, SerializationExceptions, or other failures within the RPC framework.
What you want is a checked exception, because you want to send it back to the client and do something about it. The RPC framework is in charge of catching it, serializing it and calling the onFailure method with the right exception. In order to do that, you need to follow its guidelines which are :
You need to specifiy in the service
signature that the exception can be
thrown.
The exception must be thrown
in the service implementation method.
What you're doing is throwing an unexcepted exception from some method that shouldn't even be throwing exceptions. So RPC doesn't know what really happened and sends back a generic message saying 'Hey, something unexpected happened, look at the server log'.
I understand that you want to check the session on every call. Your easiest option is to have a method to check that in your servlet implementation and call it from all your service methods.
Otherwise, you can try to override the GWT RPC framework by looking at the class
com.google.gwt.user.server.rpc.RPC
but that's pretty advanced stuff.
If you want to send exceptions via GWT-RPC you must use checked exceptions. RuntimeException is an unchecked exception so you can not use it in this case.
Create your own exception that extends Exception and implements Serializable. Also, you must indicate on methods declaration that this method might throw an exceptions: