I'm looking for some guidance on real unit testing for Restlet components, and specifically extractors. There is plenty of advice on running JUnit to rest entire endpoints, but being picky this is not unit testing, but integration testing. I really don't want to have set up an entire routing system and Spring just to check an extractor against a mock data repository.
The extractor looks like this:
public class CaseQueryExtractor extends Extractor {
protected int beforeHandle(Request request, Response response) {
extractFromQuery("offset", "offset", true);
extractFromQuery("limit", "limit", true);
// Stuff happens...
attributes.put("query", query);
return CONTINUE;
}
}
I'm thinking part of the virtue of Restlets is that its nice routing model ought to make unit testing easy, but I can't figure out what I need to do to actually exercise extractFromQuery and its friends, and all my logic that builds a query object, without mocking so much that I'm losing testing against a realistic web request.
And yes, I am using Spring, but I don't want to have to set the whole context for this -- I'm not integration testing as I haven't actually finished the app yet. I'm happy to inject manually, once I know what I need to make to get this method called.
Here's where I'm at now:
public class CaseQueryExtractorTest {
private class TraceRestlet extends Restlet {
// Does snothing, but prevents warning shouts
}
private CaseQueryExtractor extractor;
#Before
public void initialize() {
Restlet mock = new TraceRestlet();
extractor = new CaseQueryExtractor();
extractor.setNext(mock);
}
#Test
public void testBasicExtraction() {
Reference reference = new Reference();
reference.addQueryParameter("offset", "5");
reference.addQueryParameter("limit", "3");
Request request = new Request(Method.GET, reference);
Response response = extractor.handle(request);
extractor.handle(request, response);
CaseQuery query = (CaseQuery) request.getAttributes().get("query");
assertNotNull(query);
}
}
Which of course fails, as whatever set up I am doing isn't enough to make Restlets able to extract the query parameters.
Any thoughts or pointers?
There is a test module in Restlet that can provide you some hints about unit testing. See https://github.com/restlet/restlet-framework-java/tree/master/modules/org.restlet.test/src/org/restlet/test.
You can have a look at class HeaderTestCase (see https://github.com/restlet/restlet-framework-java/blob/master/modules/org.restlet.test/src/org/restlet/test/HeaderTestCase.java).
For information, if you use attributes from request, your unit test will pass ;-) See below:
public class CaseQueryExtractor extends Extractor {
protected int beforeHandle(Request request, Response response) {
extractFromQuery("offset", "offset", true);
extractFromQuery("limit", "limit", true);
// Stuff happens...
CaseQuery query = new CaseQuery();
Map<String,Object> attributes = request.getAttributes();
attributes.put("query", query);
return CONTINUE;
}
}
I don't know if you want to go further...
Hope it helps you,
Thierry
Related
I have a Java Spring Boot Application that is collecting metrics with Prometheus. It is a REST API and everything is working.
Spring, by default, provide some metrics. For example, the metric http_server_requests_seconds_count that has the dimensions:
http_server_requests_seconds_count{application="metrics-demo-app", exception="None", instance="host.docker.internal:8080", job="metrics-demo-app", method="GET", outcome="SUCCESS", status="200", uri="/actuator/prometheus"}
By dimension I mean the key/value pairs (application="metrics-demo-app", exception="None", etc...) tags in the example above.
Can I add a new dimension (key/value pair) to those metrics? My idea is when a call to create a user fails, to insert some more info into that metric.
I know that I could create a new custom metric, but I wonder if there are some manner to just add things in an existing one.
Is that possible? How to do that?
Thanks in advance :D
This can be done and how is in the documentation, here:
https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.metrics.supported.system
My code ended like this:
#Configuration
public class MetricsConfig implements WebMvcTagsProvider {
private final WebMvcTagsProvider delegate = new DefaultWebMvcTagsProvider();
#Override
public Iterable<Tag> getTags(HttpServletRequest request, HttpServletResponse response, Object handler, Throwable exception) {
final String channelId = ServiceContextHolder.getContext().getXItauChannelId();
return Tags.of(this.delegate.getTags(request, response, handler, exception)).and("channelId", channelId);
}
#Override
public Iterable<Tag> getLongRequestTags(HttpServletRequest request, Object handler) {
return null;
}
}
Hoping someone else is having the same issue as me, or has other ideas.
I'm currently running Play 1.4.x (not by choice), but also working on upgrading to play 1.5.x, though I verified the same issue happens on both versions.
I created a simple Functional Test that loads data via fixtures
My fixture for loading test data is like so
data.yml
User(testUser):
name: blah
AccessToken(accessToken):
user: testUser
token: foo
Data(testData):
user: testUser
...
I've created a controller to do something with the data like this, that has middleware for authentication check. The routes file will map something like /foo to BasicController.test
public class BasicController extends Controller{
#Before
public void doAuth(){
String token = "foo"; // Get token somehow from header
AccessToken token = AccessToken.find("token = ?", token).first(); // returns null;
// do something with the token
if(token == null){
//return 401
}
//continue to test()
}
public void test(){
User user = //assured to be logged-in user
... // other stuff not important
}
}
Finally I have my functional test like so:
public class BasicControllerTest extends FunctionalTest{
#org.junit.Before
public void loadFixtures(){
Fixtures.loadModels("data.yml");
}
#Test
public void doTest(){
Http.Request request = newRequest()
request.headers.put(...); // Add auth token to header
Http.Response response = GET(request, "/foo");
assertIsOk(response);
}
}
Now, the problem I'm running into, is that I can verify the token is still visible in the headers, but running AccessToken token = AccessToken.find("token = ?", token).first(); returns null
I verified in the functional test, before calling the GET method that the accessToken and user were created successfully from loading the fixtures. I can see the data in my, H2 in-memory database, through plays new DBBrowser Plugin in 1.5.x. But for some reason the data is not returned in the controller method.
Things I've tried
Ensuring that the fixtures are loaded only once so there is no race condition where data is cleared while reading it.
Using multiple ways of querying the database via nativeQuery jpql/hql query language and through plays native way of querying data.
Testing on different versions of play
Any help would be very much appreciated!
This issue happens on functional tests, because JPA transactions must be encapsulated in a job to ensure that the result of the transaction is visible in your method. Otherwise, since the whole functional test is run inside a transaction, the result will only visible at the end of the test (see how to setup database/fixture for functional tests in playframework for a similar case).
So you may try this:
#Test
public void doTest() {
...
AccessToken token = new Job<AccessToken>() {
#Override
public User doJobWithResult() throws Exception {
return AccessToken.find("token = ?", tokenId).first();
}
}.now().get();
....
}
Hoping it works !
I think I had a similar issue, maybe this helps someone.
There is one transaction for the functional test and a different transaction for the controller. Changes made in the test will only become visible by any further transaction if those changes were committed.
One can achieve this by closing and re-opening the transaction in the functional test like so.
// Load / Persist date here
JPA.em().getTransaction().commit(); // commit and close the transaction
JPA.em().getTransaction().begin(); // reopen (if you need it)
Now the data should be returned in the controller method.
So your test would look like this:
public class BasicControllerTest extends FunctionalTest{
#org.junit.Before
public void loadFixtures(){
Fixtures.loadModels("data.yml");
JPA.em().getTransaction().commit();
// JPA.em().getTransaction().begin(); reopen (if you need it)
}
#Test
public void doTest(){
Http.Request request = newRequest()
request.headers.put(...); // Add auth token to header
Http.Response response = GET(request, "/foo");
assertIsOk(response);
}
}
I did never try this with fixtures. But i would assume they run in the same transaction.
I'm trying to do some testing on a piece of legacy code and I have hit a bit of a wall. The code is part of a backend server for one of our angular web applications. The particular code I need to test is responsible for managing the creation of a sales rep account using data passed in from the client. In addition to saving the new rep to our mongo db, the code also has to handle saving to an external sql db. The work flow looks something like this:
Receive 'put' request from client.
Create a new 'rep' object with the passed in data.
Save the rep to a mongo db.
Call a Groovy class that will do an 'insert' on a remote db and return the id of the remote record.
Save the remote id into the mongo data.
Normally, I would use Mockito to mock out the sql connection, but I couldn't get that to work for this case. My next thought was to try mocking the Groovy class instead. I don't actually care about the internals of the groovy method, I just need an Id back. So far that has not worked either. The reason I suspect is that the groovy method is getting called from a protected method inside my service class. I don't have any control over the signature of this method, it is an override from another library.
Is there anything I can do to be able to test this code without having to set up an actual connection to the Sql db?
Web Service:
#Override
protected void beforeInsert() {
super.beforeInsert();
final Rep weakRep = this;
dwPhase1 = injector.getInstance(DwPhase1.class);
return dwPhase1.insertRepDetail(weakRep);
}
Mocking code:
DwPhase1 dwGroovy = Mockito.mock(DwPhase1.class);
Mockito.when(dwGroovy.insertRepDetail(Mockito.any(Rep.class))).then(new Answer<Object>() {
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
// TODO Auto-generated method stub
return "hello";
}
});
}
Groovy snippet:
class DwPhase1 extends SQL{
public Number insertRepDetail(Rep r){
String insert="""insert into REP_DETAIL (some values)""";
List<List<Object>> rows =null;
sql.withTransaction {
if(update!=null){
sql.executeUpdate(update);
}
rows = sql.executeInsert(params,insert.toString());
}
Number dwId = null;
if(rows!=null && !rows.isEmpty()){
List columns=rows.get(0);
if(columns!=null && !columns.isEmpty()){
dwId = columns.get(0);
}
}
return dwId;
}
}
I'm having a big problem with a project that I'm working on and I'm wondering if you could help me.
I have to perform a few unit tests using mockito, all methods go great! until you have 2 calls to http in the same method and I don't know how to differentiate them.
I have the following on the tests:
// -----------------------------------------------------------services
#InjectMocks
private SandboxAccountService accountService;
#InjectMocks
private SandboxBalancesService balancesService;
#InjectMocks
private SandboxMovementsService movementService;
#Mock
private RestTemplate restTemplate;
#Mock
private RestTemplate restTemplateMovimientos;
#Test
public void test_movementsServiceImpl() throws Exception {
//LLAMADA A LISTA DE Account
List<Account> accountList = new ArrayList<>();
accountList.add(account);
accountList.add(account2);
ResponseEntity<List<Account>> list = new ResponseEntity<List<Account>>(accountList, HttpStatus.OK);
// FIRST HTTP CALL
when(restTemplate.exchange(anyString() , any(HttpMethod.class),
any(HttpEntity.class), any(ParameterizedTypeReference.class))).thenReturn(list);
//LLAMADA A LISTA DE MOVIMIENTOS
listMovent.add(movement);
listMovent.add(movementDos);
ResponseEntity<List<Movement>> listaMovi = new ResponseEntity<List<Movement>>(listMovent, HttpStatus.OK);
// Second HTTP CALL
when(restTemplateMovimientos.exchange(anyString() , any(HttpMethod.class),
any(HttpEntity.class), any(ParameterizedTypeReference.class))).thenReturn(listaMovi);
try {
AccountsMovementsResponse accountsMovementsResponse = movementService.getMovements(accountsMovementsRequest,
AUTORIZATHION_TOKEN, language);
} catch (Exception e) {
}
}
When debug does the lists for me correctly and all well but when he switches to the service
//// This its a primary http ( Account)
ResponseEntity<List<Account>> exchange = restTemplate.exchange(sandboxAccountURL + userId, HttpMethod.GET,entity,
new ParameterizedTypeReference<List<Account>>() {
});
// This list its Account CORRECT
List<Account> lista=exchange.getBody();
// code.....
// This its a second http ( movement )
ResponseEntity<List<Movement>> movementList = restTemplate.exchange(GenerateUrl, HttpMethod.GET,entity,
new ParameterizedTypeReference<List<Movement>>() {
});
// This list should be moves, but it's a list of accounts.
List<Movement> listMovement= movementList.getBody();
My big problem is that instead of having 2 different lists I have 2 lists of them so the test can't continue.
if i try the code everything works without a problem and makes it work, the problem i have is that at the moment of testing it clones the lists.
I don't know if there is a way to make the mock's "when" can make them different because it makes me understand that it takes the first when I do.
Thank you very much for your help!
I found the solution, instead of using the key several times using the when you can make a single call and then making several returns in the order that you require attached the answer of how I work
when(restTemplate.exchange(anyString(), any(HttpMethod.class),
any(HttpEntity.class), any(ParameterizedTypeReference.class))).thenReturn(list).thenReturn(listaMovi);
i need to mock a javax.mail.Session object in my unit tests.
The class javax.mail.Session is marked final so Mockito is not able to create a mock. Does anyone have an idea how to fix this?
Edit:
My test is an Arquillian test and has already an annotation #RunWith(Arquillian.class). Therefore powermock is not an option.
You may refactor your code a little bit. In the "Working with Legacy Code" book by Martin Fowler, he describes a technique of separating the external API (think Java Mail API) from your own application code. The technique is called "Wrap and Skin" and is pretty simple.
What you should do is simply:
Create an interface MySession (which is your own stuff) by extracting methods from the javax.mail.Session class.
Implement that interface by creating a concrete class (looking a bit like the original javax.mail.Session)
In each method DELEGATE the call to equivalent javax.mail.Session method
Create your mock class which implements MySession :-)
Update your production code to use MySession instead of javax.mail.Session
Happy testing!
EDIT: Also take a look at this blog post: http://www.mhaller.de/archives/18-How-to-mock-a-thirdparty-final-class.html
Use PowerMockito to mock it.
#RunWith(PowerMockRunner.class)
// We prepare PartialMockClass for test because it's final or we need to mock private or static methods
#PrepareForTest(javax.mail.Session.class)
public class YourTestCase {
#Test
public void test() throws Exception {
PowerMockito.doReturn(value).when(classUnderTest, "methodToMock", "parameter1");
}
}
You can use the Mock JavaMail project. I first found it from Kohsuke Kawaguchi. Alan Franzoni also has a primer for it on his blog.
When you put this jar file in your classpath, it substitutes any sending of mail to in memory mailboxes that can be checked immediately. It's super easy to use.
Adding this to your classpath is admittedly a pretty heavy handed way to mock something, but you rarely want to send real emails in your automated tests anyway.
If you want to mock a final classes you can use the JDave unfinalizer which can be found here : http://jdave.org/documentation.html#mocking
It uses CGLib to alter the bytecode dynamically when the JVM is loaded to transform the class as a non final class.
This library can then be used with JMock2 ( http://www.jmock.org/mocking-classes.html ) to make your tests because as far as I know, Mockito is not compatible with JDave.
Use Java 8 Functions!
public class SendEmailGood {
private final Supplier<Message> messageSupplier;
private final Consumer<Message> messageSender;
public SendEmailGood(Supplier<Message> messageSupplier,
Consumer<Message> messageSender) {
this.messageSupplier = messageSupplier;
this.messageSender = messageSender;
}
public void send(String[] addresses, String from,
String subject, String body)
throws MessagingException {
Message message = messageSupplier.get();
for (String address : addresses) {
message.addRecipient
(Message.RecipientType.TO, new InternetAddress(address));
}
message.addFrom(new InternetAddress[]{new InternetAddress(from)});
message.setSubject(subject);
message.setText(body);
messageSender.accept(message);
}
}
Then your test code will look something like the following:
#Test
public void sendBasicEmail() throws MessagingException {
final boolean[] messageCalled = {false};
Consumer<Message> consumer = message -> {
messageCalled[0] = true;
};
Message message = mock(Message.class);
Supplier<Message> supplier = () -> message;
SendEmailGood sendEmailGood = new SendEmailGood(supplier, consumer);
String[] addresses = new String[2];
addresses[0] = "foo#foo.com";
addresses[1] = "boo#boo.com";
String from = "baz#baz.com";
String subject = "Test Email";
String body = "This is a sample email from us!";
sendEmailGood.send(addresses, from, subject, body);
verify(message).addRecipient(Message.RecipientType.TO, new InternetAddress("foo#foo.com"));
verify(message).addRecipient(Message.RecipientType.TO, new InternetAddress("boo#boo.com"));
verify(message).addFrom(new InternetAddress[]{new InternetAddress("baz#baz.com")});
verify(message).setSubject(subject);
verify(message).setText(body);
assertThat(messageCalled[0]).isTrue();
}
To create an integration test, plugin the real Session, and Transport.
Consumer<Message> consumer = message -> {
try {
Transport.send(message);
} catch (MessagingException e) {
e.printStackTrace();
}
};
Supplier<Message> supplier = () -> {
Properties properties = new Properties();
return new MimeMessage(Session.getDefaultInstance(properties));
};
See the PowerMock docs for running under JUnit 3, since it did not have runners, or use a byte-code manipulation tool.
If you can introduce Spring into your project you can use the JavaMailSender and mock that. I don't know how complicated your requirements are.
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
#Test
public void createAndSendBookChangesMail() {
// Your custom service layer object to test
MailServiceImpl service = new MailServiceImpl();
// MOCK BEHAVIOUR
JavaMailSender mailSender = mock(JavaMailSender.class);
service.setMailSender(mailSender);
// PERFORM TEST
service.createAndSendMyMail("some mail message content");
// ASSERT
verify(mailSender).send(any(SimpleMailMessage.class));
}
This is a pretty old question, but you could always implement your own Transport using the JavaMail API. With your own transport, you could just configure it according to this documentation. One benefit of this approach is you could do anything you want with these messages. Perhaps you would store them in a hash/set that you could then ensure they actually got sent in your unit tests. This way, you don't have to mock the final object, you just implement your own.
I use the mock-javamail library. It just replace the origin javamail implementation in the classpath. You can send mails normally, it just sends to in-memory MailBox, not a real mailbox.
Finally, you can use the MailBox object to assert anything you want to check.