Robolectric java.lang.IllegalArgumentException: class com.android.app.R contains final fields - junit

I am testing a legacy app with resources distributed across 3 packages.
So I extended RobolectricTestRunner and modified resource path as below.
public class DialerRobolectricTestRunner extends RobolectricTestRunner {
#Override
protected AndroidManifest getAppManifest(Config config) {
return new AndroidManifest(Fs.fileFromPath(manifestPath),
Fs.fileFromPath(resDir), Fs.fileFromPath(assetsDir)) {
#Override
public List<ResourcePath> getIncludedResourcePaths() {
List<ResourcePath> paths = super.getIncludedResourcePaths();
paths.add(new ResourcePath(getRClass(),Fs.fileFromPath("../pkg1/res/"), getAssetsDirectory()));
paths.add(new ResourcePath(getRClass(), Fs.fileFromPath("../pkg2/res/"), getAssetsDirectory()));
paths.add(new ResourcePath(getRClass(), Fs.fileFromPath("../pkg2/res/"), getAssetsDirectory()));
return paths;
}
}
}
I see below exception while testing android app with Robolectric
java.lang.IllegalArgumentException: class com.android.app.R contains final fields, these will be inlined by the compiler and cannot be remapped.
Robolectric : 3.3.2,
sdk : 23

Related

Camel - CSV Headers setting not working

I have CSV files without headers. Since I'm using 'useMaps' I want to specify the headers dynamically. If I set headers statically and then use in route it works fine as below Approach 1 -
#Component
public class BulkActionRoutes extends RouteBuilder {
#Override
public void configure() throws Exception {
CsvDataFormat csv = new CsvDataFormat(",");
csv.setUseMaps(true);
ArrayList<String> list = new ArrayList<String>();
list.add("DeviceName");
list.add("Brand");
list.add("status");
list.add("type");
list.add("features_c");
list.add("battery_c");
list.add("colors");
csv.setHeader(list);
from("direct:bulkImport")
.convertBodyTo(String.class)
.unmarshal(csv)
.split(body()).streaming()
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
GenericObjectModel model = null;
HashMap<String, String> csvRecord = (HashMap<String, String>)exchange.getIn().getBody();
}
});
}
}
However, if the list is passed via Camel headers as below then it does not work Approach 2 -
#Component
public class BulkActionRoutes extends RouteBuilder {
#Override
public void configure() throws Exception {
CsvDataFormat csv = new CsvDataFormat(",");
csv.setUseMaps(true);
from("direct:bulkImport")
.convertBodyTo(String.class)
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
ArrayList<String> fileHeaders = (ArrayList<String>)headers.get(Constants.FILE_HEADER_LIST);
if (fileHeaders != null && fileHeaders.size() > 0) {
csv.setHeader(fileHeaders);
}
}
})
.unmarshal(csv)
.split(body()).streaming()
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
GenericObjectModel model = null;
HashMap<String, String> csvRecord = (HashMap<String, String>)exchange.getIn().getBody();
}
});
}
}
What could be missing in the Approach 2?
The big difference between approach 1 and 2 is the scope.
In approach 1 you fully configure the CSV data format. This is all done when the Camel Context is created, since the data format is shared within the Camel Context. When messages are processed, it is the same config for all messages.
In approach 2 you just configure the basics globally. The header configuration is within the route and therefore can change for every single message. Every message would overwrite the header configuration of the context-global data format instance.
Without being sure about this, I guess that it is not possible to change a context-global DataFormat inside the routes.
What would you expect (just for example) when messages are processed in parallel? They would overwrite the header config against each other.
As an alternative, you could use a POJO where you can do your dynamic marshal / unmarshal from Java code.

JUnit testing of Ratpack server with Guice injection

I am trying to write a JUnit test with service dependencies being injected.
protected MainClassApplicationUnderTest aut = new MainClassApplicationUnderTest(App.class) {
#Override
protected void addImpositions(final ImpositionsSpec impositions) {
impositions.add(UserRegistryImposition.of(appRegistry -> {
// Allow modifying Injector in tests
return appRegistry.join(Guice.registry(injector));
}));
}
};
private Injector injector = com.google.inject.Guice.createInjector(new Module());
#Before
public void setup () {
injector.injectMembers(this);
}
#After
public void tearDown() {
aut.close();
}
and then using injected services in my test classes:
#Inject
private UserService userService;
This was working fine until I started adding persistence to my app with HikariModule. Now Guice registry creation is a bit more complex:
.join(Guice.registry(b -> b
.module(HikariModule.class, hikariConfig -> {
final String dbUrl = System.getenv("JDBC_DATABASE_URL");
hikariConfig.setJdbcUrl(dbUrl);
})
.module(Module.class)
.bind(DbMigrator.class)
).apply(r))
Because my registry now consists of multiple modules if I have a service that depends on DataSource class coming from HikariModule guice injection fails in tests.
My goal is to allow writing tests in the following fashion:
#Inject // <- not required can be done in #Before method
private UserService userService; // <- Inject it somehow from Application under test
#Test
public void testUser() {
final Result<User, String> userResult = userService.create(new User.Registration());
final ReceivedResponse res = aut.getHttpClient().get("/users/" + user.userId);
assertEquals(200, res.getStatusCode());
}
What is the right approach of injecting service dependencies in tests? I would very much prefer reusing guice modules from MainClassApplicationUnderTest rather than creating my own and overriding them.
After quite some time battling with this issue and help from Ratpack slack I managed to pull this off.
First of all we need to capture our application registry in the local variable.
private Registry appRegistry;
protected MainClassApplicationUnderTest aut = new MainClassApplicationUnderTest(App.class) {
#Override
protected void addImpositions(final ImpositionsSpec impositions) {
impositions.add(UserRegistryImposition.of(r -> {
appRegistry = r;
return Registry.empty();
}));
}
};
It turns out there is a nifty method that starts the application. So when injecting the class we will know that Registry will not be null and we can inject classes.
protected <T> T inject(final Class<T> classOf) {
aut.getAddress();
return appRegistry.get(classOf);
}
Then in test classes we can simply inject any class that is present in the registry.
final UserService userService = inject(UserService.class);
// OR
final DataSource dataSource = inject(DataSource.class);

Android:PowerMock work in "test" folder but not work in "androidTest" folder

I want to test 2 static methods in Android app. So I use PowerMock for this.
I write 2 tests. One unit test and one instrumented unit test.
My android build.gradle:
dependencies {
androidTestCompile "org.mockito:mockito-android:2.7.21"
androidTestCompile "org.powermock:powermock-api-mockito:1.6.6"
androidTestCompile 'org.powermock:powermock-module-junit4:1.6.6'
testCompile 'junit:junit:4.12'
testCompile 'org.powermock:powermock-api-mockito:1.6.6'
testCompile 'org.powermock:powermock-module-junit4:1.6.6'
}
Unit test is in folder: app/src/test/java/com/mycompany/StringUtilTest.java
Here code:
#RunWith(PowerMockRunner.class)
#PrepareForTest(StringUtil.class)
public class StringUtilTest {
#Test
public void isCorrectEmailStub() {
// mock all the static methods in a class called "StringUtil"
mockStatic(StringUtil.class);
String INCORRECT_EMAIL_TO_CHECK = "incorrect_email_some.com";
when(StringUtil.isCorrectEmail(INCORRECT_EMAIL_TO_CHECK)).thenReturn(true);
assertThat(StringUtil.isCorrectEmail(INCORRECT_EMAIL_TO_CHECK), is(true));
}
}
And it work fine. OK.
Instrumented unit test is in folder: app/src/androidTest/java/com/mycompany/StringUtilInstrumentedTest.java
Here code:
#RunWith(PowerMockRunner.class)
#PrepareForTest(StringUtil.class)
public class StringUtilInstrumentedTest {
private static String TAG;
private Context context;
#Before
public void init() {
TAG = StringUtilInstrumentedTest.class.getName();
context = InstrumentationRegistry.getTargetContext();
}
#Test
public void decliningAge() {
// mock all the static methods in a class called "StringUtil"
mockStatic(StringUtil.class);
// use Mockito to set up your expectation
String expected = "first";
when(StringUtil.decliningAge(context, 1)).thenReturn(expected);
assertThat(StringUtil.decliningAge(context, 1), is(expected));
}
}
But when I run this test I get error:
java.lang.ClassNotFoundException: com.mycompany.StringUtilInstrumentedTest
at java.lang.Class.classForName(Native Method)
at java.lang.Class.forName(Class.java:324)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1879)
Caused by: java.lang.IllegalStateException: Failed to transform class with name com.mycompany.StringUtilInstrumentedTest.Reason: com.mycompany.StringUtilInstrumentedTest
at org.powermock.core.classloader.MockClassLoader.loadMockClass(MockClassLoader.java:284)
at org.powermock.core.classloader.MockClassLoader.loadModifiedClass(MockClassLoader.java:192)
at org.powermock.core.classloader.DeferSupportingClassLoader.loadClass1(DeferSupportingClassLoader.java:77)
at org.powermock.core.classloader.DeferSupportingClassLoader.loadClass(DeferSupportingClassLoader.java:67)
at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
Caused by: javassist.NotFoundException: com.mycompany.StringUtilInstrumentedTest
at javassist.ClassPool.get(ClassPool.java:452)
at org.powermock.core.classloader.MockClassLoader.loadMockClass(MockClassLoader.java:262)

JAX-RS Exception Mapper not working in Grizzly container

Working on a Jersey web application with a team, as the project got bigger and bigger, we decided to switch from Tomcat to Grizzly to allow deploying parts of the project on different port numbers. What I've found out now, that the custom exception handling we have fails to work now, instead I always get the grizzly html page.
Example exception:
public class DataNotFoundException extends RuntimeException{
private static final long serialVersionUID = -1622261264080480479L;
public DataNotFoundException(String message) {
super(message);
System.out.println("exception constructor called"); //this prints
}
}
Mapper:
#Provider
public class DataNotFoundExceptionMapper implements ExceptionMapper<DataNotFoundException>{
public DataNotFoundExceptionMapper() {
System.out.println("mapper constructor called"); //doesnt print
}
#Override
public Response toResponse(DataNotFoundException ex) {
System.out.println("toResponse called"); //doesnt print
ErrorMessage errorMessage = new ErrorMessage(ex.getMessage(), 404, "No documentation yet.");
return Response.status(Status.NOT_FOUND)
.entity(errorMessage)
.build();
//ErrorMessage is a simple POJO with 2 string and 1 int field
}
}
I'm not sure where is the problem source, if needed I can provide more information/code. What's the problem, what can I try?
EDIT:
Main.class:
public class Main {
/**
* Main method.
* #param args
* #throws Exception
*/
public static void main(String[] args) throws Exception {
...
List<ServerInfo> serverList = new ArrayList<ServerInfo>();
serverList.add(new ServerInfo(
"api",8450,
new ResourceConfig().registerClasses(
the.package.was.here.ApiResource.class)
));
for(ServerInfo server : serverList) {
server.start();
}
System.out.println("Press enter to exit...");
System.in.read();
for(ServerInfo server : serverList) {
server.stop();
}
}
}
EDIT2:
based on this question I've tried using this ServerProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR, "true"property, which only helped a little. I still get the html grizzly page when the exception happens, but now I see my exception (+stack trace) in the body of the page.
You're only registering one resource class for the entire application
new ResourceConfig().registerClasses(
eu.arrowhead.core.api.ApiResource.class
)
The mapper needs to be registered also
new ResourceConfig().registerClasses(
eu.arrowhead.core.api.ApiResource.class,
YourMapper.class)
)
You can also use package scanning, which will pick up all classes and automatically register them, if they are annotated with #Path or #Provider
new ResourceConfig().packages("the.packages.to.scan")

Spring Content-Negotiation with Path Extension (set Accept-Header via extension?!)

I want to have the same URL Path handles by two different #Controller methods, which partially works. Here's what I've got:
WebMvcConfig:
public class WebMvcConfig extends WebMvcConfigurationSupport {
#Override
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter json = new MappingJackson2HttpMessageConverter();
json.setObjectMapper(objectMapper());
converters.add(json);
VCardMessageConverter vcard = new VCardMessageConverter();
converters.add(vcard);
}
#Override
protected void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(true)
.mediaType("json", MediaType.APPLICATION_JSON)
.mediaType("vcf", MediaType.TEXT_VCARD)
;
}
}
Controller:
#RestController
#RequestMapping("test")
class UserProfileController {
#RequestMapping(value="profile", method=RequestMethod.GET, produces=MediaType.TEXT_VCARD_VALUE)
VCard getProfileVCard() {
Profile p = service.getProfile();
VCard v = p.getVCard();
return v;
}
#RequestMapping(value="profile", method=RequestMethod.GET)
Profile getProfile() {
Profile p = service.getProfile();
return p;
}
}
Current behavior:
The good ones:
GET /test/profile (with Accept=*/*) calls getProfile()
GET /test/profile (with Accept=application/json) calls getProfile()
GET /test/profile.json (with Accept=*/*) calls getProfile()
GET /test/profile.json (with Accept=text/vcard) returns 406 NOT ACCEPTABLE
GET /test/profile (with Accept=text/vcard) calls getProfileVCard()
GET /test/profile.vcf (with Accept=text/vcard) calls getProfileVCard()
The faulty one:
GET /test/profile.vcf (with Accept=*/*) calls getProfile() and returns 406 NOT ACCEPTABLE.
Why does the wrong method get called? I thought I set favorPathExtension(true) in my config in order to make Spring override the Accept-Header when some Path Extension is set?
EDIT:
I now also set favorPathExtension(true).ignoreAcceptHeader(true).favorParameter(true) in my config and it still doesn't work, even profile?format=vcf and profile.vcf?format=vcf don't work