In my Junit Jupiter API 5.5 test, I am calling my method which internally makes a HTTP call to a remote service.
Now the remote service can be down or behave incorrectly. I want to skip my test in case the remote service is not behaving expectedly.
#Test
void testMe() {
// do something
Result res1 = myObject.retrieveResults(params)
// assert something
Result res2 = myObject.retrieveResults(param2)
//asert on results
}
Result retrieveResults(Parameters param) {
// do something
// call to remote service
// if they do not give result throw CustomException()
// return Result
}
So basically in my test i would want to check if myObject.retrieveResult is throwing CustomException then skip that test, otherwise evaluate normally.
We have 2 different ways to accomplish this tasks in JUnit 5.
For demo purposes, I have created a basic class which sends a request to the url
that is passed as an argument to its call(String url) method and
returns true or false depending on the request result.
The body of the method is irrelevant here.
Using Assumptions.assumeTrue()/assumeFalse() methods
Assumptions class provides us with two overloaded methods - assumeTrue
and assumeFalse. The idea is that, if the assumption is wrong, the test will be skipped.
So, the test will be something like this.
#Test
void call1() {
Assumptions.assumeTrue(new EndpointChecker(), "Endpoint is not available");
Assertions.assertTrue(HttpCaller.call("https://www.google.com"));
}
Here is the code for EndpointChecker class.
static class EndpointChecker implements BooleanSupplier {
#Override
public boolean getAsBoolean() {
// check the endpoint here and return either true or false
return false;
}
}
When the test is run, the availability of the endpoint will be checked first, if it is up, then the test will run.
Using JUnit 5 extension mechanisms.
So, let's start with creating the annotation. It is pretty straightforward.
#Retention(RetentionPolicy.RUNTIME)
#ExtendWith(EndpointAvailabilityCondition.class)
public #interface SkipWhenEndpointUnavailable {
String uri();
}
And EndpointAvailabilityCondition class. Even though, it looks big, overall logic is very simple.
import static org.junit.platform.commons.util.AnnotationUtils.findAnnotation;
public class EndpointAvailabilityCondition implements ExecutionCondition {
#Override
public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
final var optional = findAnnotation(context.getElement(), SkipWhenEndpointUnavailable.class);
if (optional.isPresent()) {
final SkipWhenEndpointUnavailable annotation = optional.get();
final String uri = annotation.uri();
// check connection here start
boolean result = false; // dummy value
// check connection here end
if (result) {
return ConditionEvaluationResult.enabled("Connection is up");
} else {
return ConditionEvaluationResult.disabled("Connection is down");
}
}
return ConditionEvaluationResult.enabled("No assumptions, moving on...");
}
}
Hence, we can do the following in our tests.
#Test
#SkipWhenEndpointUnavailable(uri = "https://www.google.com")
void call2() {
Assertions.assertTrue(HttpCaller.call("https://www.google.com"));
}
We can go ahead and add #Test annotation over #SkipWhenEndpointUnavailable and remove it from our test code. Like, so:
#Retention(RetentionPolicy.RUNTIME)
#ExtendWith(EndpointAvailabilityCondition.class)
#Test
public #interface SkipWhenEndpointUnavailable {
String uri();
}
class HttpCallerTest {
#SkipWhenEndpointUnavailable(uri = "https://www.google.com")
void call2() {
Assertions.assertTrue(HttpCaller.call("https://www.google.com"));
}
}
I hope it helps.
Related
I have some code that returns Mono<List<UserObject>>. The first thing I want to do is check the List is not empty, and if it is, throw a NoUsersFoundException. My code looks like this:
IUserDao.java
Mono<List<UserAccount>> getUserProfiles(final Set<UserQueryFilter> filters,
final Set<String> attributes);
GetUserAccount.java
public Mono<UserAccount> doGetUserAccount() {
return userDao.getUserProfiles(filters, attributes)
.map(list -> {
if (CollectionUtils.isEmpty(list)) {
throw new NoUsersFoundException();
}
return list;
})
.map(this::removePermissions)
.map(this::removeDuplicates);
}
I want to write a unit test that will test that the NoUsersFoundException is thrown when userDao.getUserProfiles(filters, attributes) returns an empty list. When I use Mockito#when with a .thenReturn(), the test will, as expected, return immediately once userDao.getUserProfiles(...) is called without continuing the flow into the .map() where the list is checked and exception thrown.
#Mock
private IUserDao userDao;
private UserPolicies userPolicies;
#BeforeEach
public void init() {
userPolicies = new UserPolicies(Set.of("XYZ", USER_AFF, "123"),
Set.of(TestUserConstants.ID, TestUserConstants.SUBSCRIPTION_LEVEL));
}
#Test
void shouldThrowExceptionIfNoUsersFound() {
final Set<UserFilter> filters = new UserFilterBuilder().withId(ID)
.withSubscription(PREMIUM)
.build();
when(userDao.getUserProfiles(filters, userPolicies.getUserAttributeIds()))
.thenReturn(Mono.just(Collections.emptyList()));
testClass = new GetUserAccount(userDao,
userPolicies,
filters,
userPolicies.getUserAttributeIds());
assertThatThrownBy(() -> testClass.doGetUserAccount()).isInstanceOf(NoUsersFoundException.class);
}
I have tried .thenAnswer() but it essentially does the same thing as the method called is not a void:
userDao.getUserProfiles(filters, userPolicies.getUserAttributeIds()))
.thenAnswer((Answer<Mono<List>>) invocationOnMock -> Mono.just(Collections.emptyList()));
I can't see how using reactor.test.StepVerifier would work for this case.
i dont really understand what you are asking for, but we commonly dont "throw" exceptions in reactor. We return a Mono#error downstream, and different operators will react accordingly as the error travels downstream.
public Mono<List<Foobar> fooBar(filters, attributes) {
return daoObject.getUserProfiles(filters, attributes)
.map(list -> {
if (CollectionUtils.isEmpty(list)) {
// Return a mono#error
return Mono.error( ... );
}
return list;
})
}
And then test using the step verifier. With either expectNext or expectError.
// Happy case
StepVerifier.create(
fooBar(filters, attributes))
.expectNext( ... )
.verify();
// Sad case
StepVerifier.create(
fooBar(filters, attributes))
.expectError( ... )
.verify();
How to write junit test for testing the block of function that has the db call and the subsequent lines based on the db call result? any samples or example is appreciated
My function to test:
public String functionality(String input)
{
String readDBValue = db.giveMeValue();
if (input.contains(readDBValue))
{
return input;
}
else
{
return null;
}
}
My Junit4 test case:
public class JunitTest
{
#Test
public void testFunctionality()
{
String inputTst = "new input";
String expectTst = "new input";
assertEquals(functionality(input), expectTst);
}
}
How to test the line in functionality that returns some value from database from a dependent function?
Can you mock the DB and inject the mock into whatever class you are testing?
Using a test double of some description for the database will speed up your tests and allow you to specify the behaviour of the database calls so you can test branches in your code.
Edited 2019-04-28 18:17:00+13:00: Adding a code sample to illustrate.
import static org.mockito.Mockito.*;
import static org.junit.Assert.*;
public class TestMyClass {
#Test
public void test1() {
// create mock
MyDatabase mockDb = mock(MyDatabase.class);
// set behaviour required in test
when(mockDb.giveMeValue()).thenReturn("new input");
// inject mock into object being tested
MyClass objectUnderTest = new MyClass(mockDb);
// use mock in test....
assertEquals(objectUnderTest.functionality("input"), "new input");
}
}
I trying to repeat login_logout.class 10 times. Why can't I repeat this 10 times?
#RunWith(Suite.class)
#SuiteClasses({login_logout.class})
public class AllTests {
#Parameters
public static Collection<Object[]> getData(){
Object[][] data = new Object[10][0];
return Arrays.asList(data);
}
}
My understanding is "new Object[10][0]" is to construct an two-dimension array:
(1) [10][0] mean the array has 10 lines that each line holds zero object
==> Is it supposed to be "[10][1]" ?
(2) As only the code you uploaded, you didn't actually create any real object,
because the "new Object[10][0]" only make an array not any real object
The Suite test runner doesn't support parameters. You could rewrite login_logout as a parameterized test:
#RunWith(Parameterized.class)
public class LoginLogoutTest {
#Parameterized.Parameters
public static Collection<Object[]> values() {
return Arrays.asList(
new Object[10][1]);
}
public LoginLogoutTest(Object ignored) {
}
#Test
public void doSomething() {
}
}
That being said, I can't think of too many use cases for running the exact same test ten times.
Say I have a constructor where it's initialization can potentially throw an exception due to reasons beyond my control.
FantasticApiController(IAwesomeGenerator awesome,
IBusinessRepository repository, IIceCreamFactory factory)
{
Awesome = awesome;
Repository = repository;
IceCream = factory.MakeIceCream();
DoSomeInitialization(); // this can throw an exception
}
Ordinarily, when a Controller action in WebAPI throws an exception I can handle it via a csutom ExceptionFilterAttribute:
public class CustomErrorHandler
{
public override void OnException(HttpActionExecutedContext context)
{
// Critical error, this is real bad.
if (context.Exception is BubonicPlagueException)
{
Log.Error(context.Exception, "CLOSE EVERYTHING!");
Madagascar.ShutdownAllPorts();
}
// No big deal, just show something user friendly
throw new HttpResponseException(new HttpResponseMessage
{
Content = new StringContent("Hey something bad happened. " +
"Not closing the ports though"),
StatusCode = HttpStatusCode.InternalServerError;
});
}
So if I have a have a BoardPlane API method which throws a BubonicPlagueException, then my CustomerErrorHandler will shut down the ports to Madagascar and log it as an error as expected. In other instances when it's not really serious, I just display some user friendly message and return a 500 InternalServerError.
But in those cases where DoSomeInitialization throws an exception, this does absolutely nothing. How can I handle exceptions in WebAPI controller constructors?
The WebApi Controllers are created, and thus constructors called via HttpControllerActivators. The default activator is System.Web.Http.Dispatcher.DefaultHttpControllerActivator.
Very rough examples for options 1 & 2 on github here https://github.com/markyjones/StackOverflow/tree/master/ControllerExceptionHandling/src
Option 1 which works quite nicely involves the use of a DI container (you may well be using one already). I have used Ninject for my example and have used "Interceptors" Read More to intercept and try/catch calls to the Create method on the DefaultHttpControllerActivator. I know of at least AutoFac and Ninject that can do something simlar to to the following:
Create the interceptor
I don't know what the lifetime scope of your Madagascar and Log items are but they could well be injected into your Interceptor
public class ControllerCreationInterceptor : Ninject.Extensions.Interception.IInterceptor
{
private ILog _log;
private IMadagascar _madagascar;
public ControllerCreationInterceptor(ILog log, IMadagascar madagascar)
{
_log = log;
_madagascar = madagascar;
}
But keeping to the example in your question where Log and Madagascar are some kind of Static global
public class ControllerCreationInterceptor : Ninject.Extensions.Interception.IInterceptor
{
public void Intercept(Ninject.Extensions.Interception.IInvocation invocation)
{
try
{
invocation.Proceed();
}
catch(InvalidOperationException e)
{
if (e.InnerException is BubonicPlagueException)
{
Log.Error(e.InnerException, "CLOSE EVERYTHING!");
Madagascar.ShutdownAllPorts();
//DO SOMETHING WITH THE ORIGIONAL ERROR!
}
//DO SOMETHING WITH THE ORIGIONAL ERROR!
}
}
}
FINALLY Register the interceptor In global asax or App_Start (NinjectWebCommon)
kernel.Bind<System.Web.Http.Dispatcher.IHttpControllerActivator>()
.To<System.Web.Http.Dispatcher.DefaultHttpControllerActivator>().Intercept().With<ControllerCreationInterceptor>();
Option 2 is to implement your own Controller Activator implementing the IHttpControllerActivator interface and handle the error in creation of the Controller in the Create method. You could use the decorator pattern to wrap the DefaultHttpControllerActivator:
public class YourCustomControllerActivator : IHttpControllerActivator
{
private readonly IHttpControllerActivator _default = new DefaultHttpControllerActivator();
public YourCustomControllerActivator()
{
}
public System.Web.Http.Controllers.IHttpController Create(System.Net.Http.HttpRequestMessage request, System.Web.Http.Controllers.HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
try
{
return _default.Create(request, controllerDescriptor, controllerType);
}
catch (InvalidOperationException e)
{
if (e.InnerException is BubonicPlagueException)
{
Log.Error(e.InnerException, "CLOSE EVERYTHING!");
Madagascar.ShutdownAllPorts();
//DO SOMETHING WITH THE ORIGIONAL ERROR!
}
//DO SOMETHING WITH THE ORIGIONAL ERROR!
return null;
}
}
}
Once you have your own custom activator the default activator can be switched out in the global asax :
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new YourCustomControllerActivator());
Option 3 Of course if your initialisation in the constructor doesn't need access to the actual Controllers methods, properties etc... i.e. assuming it could be removed from the constructor... then it would be far easier to just move the initialisation to a filter e.g.
public class MadagascarFilter : AbstractActionFilter
{
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
try{
DoSomeInitialization(); // this can throw an exception
}
catch(BubonicPlagueException e){
Log.Error(e, "CLOSE EVERYTHING!");
Madagascar.ShutdownAllPorts();
//DO SOMETHING WITH THE ERROR
}
base.OnActionExecuting(actionContext);
}
public override void OnActionExecuted(System.Web.Http.Filters.HttpActionExecutedContext actionExecutedContext)
{
base.OnActionExecuted(actionExecutedContext);
}
public override bool AllowMultiple
{
get { return false; }
}
}
I am writing integration tests to test existing Routes. The recommended way of getting the response looks something like this (via Camel In Action section 6.4.1):
public class TestGetClaim extends CamelTestSupport {
#Produce(uri = "seda:getClaimListStart")
protected ProducerTemplate producer;
#Test
public void testNormalClient() {
NotifyBuilder notify = new NotifyBuilder(context).whenDone(1).create();
producer.sendBody(new ClientRequestBean("TESTCLIENT", "Y", "A"));
boolean matches = notify.matches(5, TimeUnit.SECONDS);
assertTrue(matches);
BrowsableEndpoint be = context.getEndpoint("seda:getClaimListResponse", BrowsableEndpoint.class);
List<Exchange> list = be.getExchanges();
assertEquals(1, list.size());
System.out.println("***RESPONSE is type "+list.get(0).getIn().getBody().getClass().getName());
}
}
The test runs but I get nothing back. The assertTrue(matches) fails after the 5 second timeout.
If I rewrite the test to look like this I get a response:
#Test
public void testNormalClient() {
producer.sendBody(new ClientRequestBean("TESTCLIENT", "Y", "A"));
Object resp = context.createConsumerTemplate().receiveBody("seda:getClaimListResponse");
System.out.println("***RESPONSE is type "+resp.getClass().getName());
}
The documentation is a little light around this so can anyone tell me what I am doing wrong with the first approach? Is there anything wrong with following the second approach instead?
Thanks.
UPDATE
I have broken this down and it looks like the problem is with the mix of seda as the start endpoint in combination with the use of a recipientList in the Route. I've also changed the construction of the NotifyBuilder (I had the wrong endpoint specified).
If I change the start endpoint to
direct instead of seda then the test will work; or
If I comment out the recipientList
then the test will work.
Here's a stripped down version of my Route that reproduces this issue:
public class TestRouteBuilder extends RouteBuilder {
#Override
public void configure() throws Exception {
// from("direct:start") //works
from("seda:start") //doesn't work
.recipientList(simple("exec:GetClaimList.bat?useStderrOnEmptyStdout=true&args=${body.client}"))
.to("seda:finish");
}
}
Note that if I change the source code of the NotifyTest from the "Camel In Action" source to have a route builder like this then it also fails.
Try to use "seda:getClaimListResponse" in the getEndpoint to be sure the endpoint uri is 100% correct
FWIW: It appears that notifyBuilder in conjunction with seda queues are not quite working: a test class to illustrate:
public class NotifyBuilderTest extends CamelTestSupport {
// Try these out!
// String inputURI = "seda:foo"; // Fails
// String inputURI = "direct:foo"; // Passes
#Test
public void testNotifyBuilder() {
NotifyBuilder b = new NotifyBuilder(context).from(inputURI)
.whenExactlyCompleted(1).create();
assertFalse( b.matches() );
template.sendBody(inputURI, "Test");
assertTrue( b.matches() );
b.reset();
assertFalse( b.matches() );
template.sendBody(inputURI, "Test2");
assertTrue( b.matches() );
}
#Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from(inputURI).to("mock:foo");
}
};
}
}