How to clean autowired object after each test execution. In my example TestClassSettings object does not get clean property and it uses the previous test class value. Here is the example:
TestClassSettings.java
#Component
public class TestClassSettings{
private String titleUnitCode = "99";
private String fte = "1";
private String testIndicator = "";
public String getTitleUnitCode() {
return titleUnitCode;
}
public void setTitleUnitCode(String titleUnitCode) {
this.titleUnitCode = titleUnitCode;
}
public String getFte() {
return fte;
}
public void setFte(String fte) {
this.fte = fte;
}
public String getTestIndicator() {
return testIndicator;
}
public void setTestIndicator(String testIndicator) {
this.testIndicator = testIndicator;
}
}
testClassSettings instance is not getting clean in between test cases.
TestLeaveHourCal_bweh6.java
#TestMethodOrder(OrderAnnotation.class)
#SpringJUnitWebConfig(locations = { "classpath:service.xml"})
#TestInstance(Lifecycle.PER_CLASS)
public class TestLeaveHourCal_bweh6 {
#Autowired
private ApproveTimesheetService approveTimesheetService;
#Autowired
private ComparePayUpdates comparePayUpdates;
#Autowired
public TestClassSettings testClassSettings; /* variable access type needs public */;
#Autowired
#RegisterExtension
protected CreateTimesheetBeforeTestExecutionCallback beforeTestExecutionCallback; /* can not be private */
#BeforeAll
public void setup() throws Exception {
/* START SETTINGS */
testClassSettings.setTestIndicator("6");
testClassSettings.setTitleUnitCode("99");
testClassSettings.setFte("0.5");
/* END SETTINGS */
}
#Test
#Order(1)
#Tag("setBaselinePayUpdates")
public void setBaselinePayUpdates() throws Exception {
}
Added #DirtiesContext(classMode = ClassMode.AFTER_CLASS) to fix the issue
Related
Converted a java class into kotlin in Android app, the jacoco coverage starts to show 0 coverage on a compiler generated function, which is not access able. Other ones seem fine in the report.
How to make reference type for string enum in kotlin and jacoco coverage testable
java code:
public final class Message {
private Message() { }
public static class MessageAction {
public static final String OPEN = "open";
public static final String VIEW_ALL = "view_all";
#Retention(RetentionPolicy.SOURCE)
#StringDef({OPEN, VIEW_ALL})
public #interface Action { }
public String mAction;
public MessageAction(#Action String action) {
this.mAction = action;
}
public String getMessageAction() {
return this.mAction;
}
}
}
in kotlin;
import androidx.annotation.StringDef
class Message private constructor() {
class MessageAction(#param:Action var messageAction: String) {
#kotlin.annotation.Retention(AnnotationRetention.SOURCE)
#StringDef(OPEN, VIEW_ALL)
annotation class Action
companion object {
const val OPEN = "open"
const val VIEW_ALL = "view_all"
}
}
}
this is sample of how it is used in java code:
public static void doSomeThing(#Nullable String message, #Message.MessageAction.Action String action) {
...
}
and the test:
#Test
public void test_messageAction() {
String testAction = "open";
Message.MessageAction action = new Message.MessageAction(Message.MessageAction.OPEN);
assertEquals(testAction, action.getMessageAction());
}
the jacoco test coverage result shows 0 covergate on the function setMessageAction(#NotNull String var1) which is in the decompiled java code.
And it is not visible from the code's autocomplete hint.
the kotlin decompiled java code:
public final class Message {
private Message() {
}
#Metadata( ...... )
public static final class MessageAction {
#NotNull
private String messageAction;
#NotNull
public static final String OPEN = "open";
#NotNull
public static final String VIEW_ALL = "view_all";
public static final Message.MessageAction.Companion Companion = new Message.MessageAction.Companion((DefaultConstructorMarker) null);
#NotNull
public final String getMessageAction() {
return this.messageAction;
}
public final void setMessageAction(#NotNull String var1) { //<=== coverage result shows it is not called
Intrinsics.checkNotNullParameter(var1, "<set-?>");
this.messageAction = var1;
}
public MessageAction(#NotNull String messageAction) {
Intrinsics.checkNotNullParameter(messageAction, "messageAction");
super();
this.messageAction = messageAction;
}
#Retention(AnnotationRetention.SOURCE)
#java.lang.annotation.Retention(RetentionPolicy.SOURCE)
#Metadata( ...... )
public #interface Action {
}
#Metadata( ...... )
public static final class Companion {
private Companion() {
}
// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
}
adding #JvmField resolves it
class MessageAction(#param:Action messageAction: String) {
#kotlin.annotation.Retention(AnnotationRetention.SOURCE)
#StringDef(OPEN, VIEW_ALL, CLEAR, TOUCH3D, PLAY, PAUSE, DISMISSED)
annotation class Action
#JvmField
var messageAction: String = messageAction
companion object {
const val OPEN = "open"
const val VIEW_ALL = "view_all"
}
}
I use Spring Cloud's ResourceLoader to access S3, e.g.:
public class S3DownUpLoader {
private final ResourceLoader resourceLoader;
#Autowired
public S3DownUpLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
public String storeOnS3(String filename, byte[] data) throws IOException {
String location = "s3://" + bucket + "/" + filename;
WritableResource writeableResource = (WritableResource) this.resourceLoader.getResource(location);
FileCopyUtils.copy( data, writeableResource.getOutputStream());
return filename;
}
It works okey and I need help to test the code with Localstack/Testcontainers. I've tried following test, but it does not work - my production profile gets picked up(s3 client with localstack config is not injected):
#RunWith(SpringRunner.class)
#SpringBootTest
public class S3DownUpLoaderTest {
#ClassRule
public static LocalStackContainer localstack = new LocalStackContainer().withServices(S3);
#Autowired
S3DownUpLoader s3DownUpLoader;
#Test
public void testA() {
s3DownUpLoader.storeOnS3(...);
}
#TestConfiguration
#EnableContextResourceLoader
public static class S3Configuration {
#Primary
#Bean(destroyMethod = "shutdown")
public AmazonS3 amazonS3() {
return AmazonS3ClientBuilder
.standard()
.withEndpointConfiguration(localstack.getEndpointConfiguration(S3))
.withCredentials(localstack.getDefaultCredentialsProvider())
.build();
}
}
}
as we discussed on GitHub,
We solve this problem in a slightly different way. I've actually never seen the way you use the WritableResource, which looks very interesting. None the less, this is how we solve this issue:
#RunWith(SpringRunner.class)
#SpringBootTest(properties = "spring.profiles.active=test")
#ContextConfiguration(classes = AbstractAmazonS3Test.S3Configuration.class)
public abstract class AbstractAmazonS3Test {
private static final String REGION = Regions.EU_WEST_1.getName();
/**
* Configure S3.
*/
#TestConfiguration
public static class S3Configuration {
#Bean
public AmazonS3 amazonS3() {
//localstack docker image is running locally on port 4572 for S3
final String serviceEndpoint = String.format("http://%s:%s", "127.0.0.1", "4572");
return AmazonS3Client.builder()
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(serviceEndpoint, REGION))
.withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials("dummyKey", "dummySecret")))
.build();
}
}
}
And a sample test:
public class CsvS3UploadServiceIntegrationTest extends AbstractAmazonS3Test {
private static final String SUCCESS_CSV = "a,b";
private static final String STANDARD_STORAGE = "STANDARD";
#Autowired
private AmazonS3 s3;
#Autowired
private S3ConfigurationProperties properties;
#Autowired
private CsvS3UploadService service;
#Before
public void setUp() {
s3.createBucket(properties.getBucketName());
}
#After
public void tearDown() {
final String bucketName = properties.getBucketName();
s3.listObjects(bucketName).getObjectSummaries().stream()
.map(S3ObjectSummary::getKey)
.forEach(key -> s3.deleteObject(bucketName, key));
s3.deleteBucket(bucketName);
}
#Test
public void uploadSuccessfulCsv() {
service.uploadSuccessfulCsv(SUCCESS_CSV);
final S3ObjectSummary s3ObjectSummary = getOnlyFileFromS3();
assertThat(s3ObjectSummary.getKey(), containsString("-success.csv"));
assertThat(s3ObjectSummary.getETag(), is("b345e1dc09f20fdefdea469f09167892"));
assertThat(s3ObjectSummary.getStorageClass(), is(STANDARD_STORAGE));
assertThat(s3ObjectSummary.getSize(), is(3L));
}
private S3ObjectSummary getOnlyFileFromS3() {
final ObjectListing listing = s3.listObjects(properties.getBucketName());
final List<S3ObjectSummary> objects = listing.getObjectSummaries();
assertThat(objects, iterableWithSize(1));
return Iterables.getOnlyElement(objects);
}
}
And the code under test:
#Service
#RequiredArgsConstructor
#EnableConfigurationProperties(S3ConfigurationProperties.class)
public class CsvS3UploadServiceImpl implements CsvS3UploadService {
private static final String CSV_MIME_TYPE = CSV_UTF_8.toString();
private final AmazonS3 amazonS3;
private final S3ConfigurationProperties properties;
private final S3ObjectKeyService s3ObjectKeyService;
#Override
public void uploadSuccessfulCsv(final String source) {
final String key = s3ObjectKeyService.getSuccessKey();
doUpload(source, key, getObjectMetadata(source));
}
private void doUpload(final String source, final String key, final ObjectMetadata metadata) {
try (ReaderInputStream in = new ReaderInputStream(new StringReader(source), UTF_8)) {
final PutObjectRequest request = new PutObjectRequest(properties.getBucketName(), key, in, metadata);
amazonS3.putObject(request);
} catch (final IOException ioe) {
throw new CsvUploadException("Unable to upload " + key, ioe);
}
}
private ObjectMetadata getObjectMetadata(final String source) {
final ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentType(CSV_MIME_TYPE);
metadata.setContentLength(source.getBytes(UTF_8).length);
metadata.setContentMD5(getMD5ChecksumAsBase64(source));
metadata.setSSEAlgorithm(SSEAlgorithm.KMS.getAlgorithm());
return metadata;
}
private String getMD5ChecksumAsBase64(final String source) {
final HashCode md5 = Hashing.md5().hashString(source, UTF_8);
return Base64.getEncoder().encodeToString(md5.asBytes());
}
}
It seems the only way to provide custom amazonS3 bean for ResourceLoader is to inject it manually. The test looks like
#RunWith(SpringRunner.class)
#SpringBootTest
#ContextConfiguration(classes = S3DownUpLoaderTest.S3Configuration.class)
public class S3DownUpLoaderTest implements ApplicationContextAware {
private static final String BUCKET_NAME = "bucket";
#ClassRule
public static LocalStackContainer localstack = new LocalStackContainer().withServices(S3);
#Autowired
S3DownUpLoader s3DownUpLoader;
#Autowired
SimpleStorageProtocolResolver resourceLoader;
#Autowired
AmazonS3 amazonS3;
#Before
public void setUp(){
amazonS3.createBucket(BUCKET_NAME);
}
#Test
public void someTestA() throws IOException {
....
}
#After
public void tearDown(){
ObjectListing object_listing = amazonS3.listObjects(QLM_BUCKET_NAME);
while (true) {
for (S3ObjectSummary summary : object_listing.getObjectSummaries()) {
amazonS3.deleteObject(BUCKET_NAME, summary.getKey());
}
// more object_listing to retrieve?
if (object_listing.isTruncated()) {
object_listing = amazonS3.listNextBatchOfObjects(object_listing);
} else {
break;
}
};
amazonS3.deleteBucket(BUCKET_NAME);
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (applicationContext instanceof ConfigurableApplicationContext) {
ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) applicationContext;
configurableApplicationContext.addProtocolResolver(this.resourceLoader);
}
}
public static class S3Configuration {
#Bean
public S3DownUpLoader s3DownUpLoader(ResourceLoader resourceLoader){
return new S3DownUpLoader(resourceLoader);
}
#Bean(destroyMethod = "shutdown")
public AmazonS3 amazonS3() {
return AmazonS3ClientBuilder
.standard()
.withEndpointConfiguration(localstack.getEndpointConfiguration(S3))
.withCredentials(localstack.getDefaultCredentialsProvider())
.build();
}
#Bean
public SimpleStorageProtocolResolver resourceLoader(){
return new SimpleStorageProtocolResolver(amazonS3());
}
}
I'm continuing on with a logging behavior using the WebSocketBehavior. It currently logs the correct data to a console, but also throws a terrible serialization error. It is because I am providing the WicketApplication itself as a constructor argument for the behavior. I've tried passing it my session object and using that to get the WebApplication, but it consistently returns null. The broadcaster object requires the application in order to function properly. My question is how can I provide the WebApplication to the behavior while avoiding the nasty serialization error?? Here is my behavior class:
public class LogWebSocketBehavior extends WebSocketBehavior implements Serializable {
private static final long serialVersionUID = 1L;
private Console console;
private Handler logHandler;
private Model<LogRecord> model = new Model<>();
private WebApplication application;
public LogWebSocketBehavior(Console console, WebApplication application) {
super();
configureLogger();
this.console = console;
this.application = application;
}
private void configureLogger() {
Enumeration<String> list = LogManager.getLogManager().getLoggerNames();
list.hasMoreElements();
Logger l = Logger.getLogger(AppUtils.loggerName);
l.addHandler(getLoggerHandler());
}
#Override
protected synchronized void onPush(WebSocketRequestHandler handler, IWebSocketPushMessage message) {
LogRecord r = model.getObject();
sendRecordToConsole(handler, r);
}
private Handler getLoggerHandler() {
return new LogHandler() {
private static final long serialVersionUID = 1L;
#Override
public void publish(LogRecord record) {
model.setObject(record);
sendToAllConnectedClients("data");
}
};
}
private synchronized void sendToAllConnectedClients(String message) {
IWebSocketConnectionRegistry registry = new SimpleWebSocketConnectionRegistry();
WebSocketPushBroadcaster b = new WebSocketPushBroadcaster(registry);
b.broadcastAll(application, new Message());
}
private void sendRecordToConsole(WebSocketRequestHandler handler, LogRecord r) {
Level level = r.getLevel();
if (level.equals(Level.INFO)) {
console.info(handler, new SimpleFormatter().formatMessage(r));
} else {
console.error(handler, new SimpleFormatter().formatMessage(r));
}
}
class Message implements IWebSocketPushMessage {
public Message() {
}
}
}
Here is the panel that is being used to display the messages:
public class FooterPanel extends Panel {
private static final long serialVersionUID = 1L;
private Form form;
private Console console;
public FooterPanel(String id) {
super(id);
}
#Override
public void onInitialize() {
super.onInitialize();
form = new Form("form");
form.add(console = getConsole("feedback_console"));
console.setOutputMarkupId(true);
form.setOutputMarkupId(true);
add(form);
add(getLoggingBehavior());
}
private Console getConsole(String id) {
return new Console(id) {
private static final long serialVersionUID = 1L;
};
}
private WebSocketBehavior getLoggingBehavior() {
return new LogWebSocketBehavior(console, this.getWebApplication());
}
}
I updated my behavior as follows:
public class LogWebSocketBehavior extends WebSocketBehavior implements Serializable {
private static final long serialVersionUID = 1L;
private Console console;
private Handler logHandler;
private Model<LogRecord> model = new Model<>();
public LogWebSocketBehavior(Console console) {
super();
configureLogger();
this.console = console;
}
private void configureLogger() {
Enumeration<String> list = LogManager.getLogManager().getLoggerNames();
list.hasMoreElements();
Logger l = Logger.getLogger(AppUtils.loggerName);
l.addHandler(getLoggerHandler());
}
#Override
protected synchronized void onPush(WebSocketRequestHandler handler, IWebSocketPushMessage message) {
LogRecord r = model.getObject();
sendRecordToConsole(handler, r);
}
private Handler getLoggerHandler() {
return new LogHandler() {
private static final long serialVersionUID = 1L;
#Override
public void publish(LogRecord record) {
model.setObject(record);
sendToAllConnectedClients("data");
}
};
}
private synchronized void sendToAllConnectedClients(String message) {
WebApplication application = WebApplication.get();
IWebSocketConnectionRegistry registry = new SimpleWebSocketConnectionRegistry();
WebSocketPushBroadcaster b = new WebSocketPushBroadcaster(registry);
b.broadcastAll(application, new Message());
}
private void sendRecordToConsole(WebSocketRequestHandler handler, LogRecord r) {
Level level = r.getLevel();
String message = AppUtils.consoleDateTimeFormat.format(LocalDateTime.now()) + " - " + AppUtils.LogFormatter.formatMessage(r);
if (level.equals(Level.INFO)) {
console.info(handler, message);
} else {
console.error(handler, message);
}
}
class Message implements IWebSocketPushMessage {
public Message() {
}
}
}
And I'm back to the original issues I started with, which is the following error:
ERROR - ErrorLogger - Job (report.DB5E002E046235586592E7E984338DEE3 : 653 threw an exception.
org.quartz.SchedulerException:
Job threw an unhandled exception. [See nested exception: org.apache.wicket.WicketRuntimeException: There is no application attached to current thread DefaultQuartzScheduler_Worker-1]
at org.quartz.core.JobRunShell.run(JobRunShell.java:213)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573)
Caused by: org.apache.wicket.WicketRuntimeException: There is no application attached to current thread DefaultQuartzScheduler_Worker-1
at org.apache.wicket.Application.get(Application.java:236)
at org.apache.wicket.protocol.http.WebApplication.get(WebApplication.java:160)
at eb.wicket.behaviors.LogWebSocketBehavior.sendToAllConnectedClients(LogWebSocketBehavior.java:77)
at eb.wicket.behaviors.LogWebSocketBehavior.access$100(LogWebSocketBehavior.java:29)
at eb.wicket.behaviors.LogWebSocketBehavior$1.publish(LogWebSocketBehavior.java:70)
Finally working as desired.. Here's the behavior class:
public class LogWebSocketBehavior extends WebSocketBehavior implements Serializable {
private static final long serialVersionUID = 1L;
private Console console;
private Model<LogRecord> model = new Model<>();
public LogWebSocketBehavior(Console console) {
super();
configureLogger();
this.console = console;
}
private void configureLogger() {
Enumeration<String> list = LogManager.getLogManager().getLoggerNames();
list.hasMoreElements();
Logger l = Logger.getLogger(AppUtils.loggerName);
l.addHandler(getLoggerHandler());
}
#Override
protected synchronized void onPush(WebSocketRequestHandler handler, IWebSocketPushMessage message) {
LogRecord r = model.getObject();
sendRecordToConsole(handler, r);
}
private Handler getLoggerHandler() {
return new LogHandler() {
private static final long serialVersionUID = 1L;
#Override
public void publish(LogRecord record) {
model.setObject(record);
sendToAllConnectedClients("data");
}
};
}
private synchronized void sendToAllConnectedClients(String message) {
IWebSocketConnectionRegistry registry = new SimpleWebSocketConnectionRegistry();
WebSocketPushBroadcaster b = new WebSocketPushBroadcaster(registry);
b.broadcastAll(Application.get("eb.wicket.MyWicketFilter"), new Message());
}
private void sendRecordToConsole(WebSocketRequestHandler handler, LogRecord r) {
Level level = r.getLevel();
String message = AppUtils.consoleDateTimeFormat.format(LocalDateTime.now()) + " - " + AppUtils.LogFormatter.formatMessage(r);
if (level.equals(Level.INFO)) {
console.info(handler, message);
} else {
console.error(handler, message);
}
}
class Message implements IWebSocketPushMessage {
public Message() {
}
}
}
Instead of keeping a reference to the Application just look it up when needed: Application.get().
After updating your question we can see:
Caused by: org.apache.wicket.WicketRuntimeException:
There is no application attached to current thread DefaultQuartzScheduler_Worker-1
This explains it - this is a thread started by Quartz, it is not a http thread.
The only way to overcome this is to use Application.get(String). The value should be the application name (Application#getName()) that is specified as a value for <filter-name> in your web.xml.
This way you can get the Application instance, but there is no way to do the same for Session and/or RequestCycle in case you need them too.
AutoMapper Version Used : 3.3.10
[TestClass]
public class AppControllerTests
{
private IMappingEngine _mappingEngine = null;
private ConfigurationStore _configurationStore = null;
[TestInitialize]
public void SetUp()
{
_configurationStore = new ConfigurationStore(new TypeMapFactory(), MapperRegistry.Mappers);
_configurationStore.AddProfile(new AutoMapperProfile.AppProfile());
_mappingEngine = new MappingEngine(_configurationStore);
}
[TestMethod]
public void GetAppByAccountID()
{
// Error line
var mappingResult = _mappingEngine.Map<Category>(categoryList).AsQueryable();
}
}
public class AppProfile : Profile
{
protected override void Configure()
{
AutoMapperMappingConfigurations();
}
public void AutoMapperMappingConfigurations()
{
Mapper.CreateMap<DomainModels.Category, Category>().ReverseMap();
}
}
Exception:
An exception of type 'AutoMapper.AutoMapperMappingException'
occurred in AutoMapper.dll but was not handled in user code.
Suspect the
_configurationStore.AddProfile(new OOS.PresentationModelService.AutoMapperProfile.AppProfile());
is not able to create an istance of AppProfile if i write the manual mapping it's working as expected.
_configurationStore.CreateMap<Category, Category>().ReverseMap();
How can I test one PUT request with Spring Boot??
I have this method:
#RequestMapping(method = RequestMethod.PUT, value = "/")
public NaturezaTitulo save(#RequestBody NaturezaTitulo naturezaTitulo){
return naturezaTituloService.save(naturezaTitulo);
}
and this test class:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
public class NaturezaTituloControllerTest {
private MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
MediaType.APPLICATION_JSON.getSubtype(),
Charset.forName("utf8"));
private MockMvc mockMvc;
private HttpMessageConverter mappingJackson2HttpMessageConverter;
private List<NaturezaTitulo> naturezaTituloList = new ArrayList<>();
#Autowired
private WebApplicationContext webApplicationContext;
#Autowired
void setConverters(HttpMessageConverter<?>[] converters) {
this.mappingJackson2HttpMessageConverter = Arrays.asList(converters).stream().filter(
hmc -> hmc instanceof MappingJackson2HttpMessageConverter).findAny().get();
Assert.assertNotNull("the JSON message converter must not be null",
this.mappingJackson2HttpMessageConverter);
}
#Before
public void setup() throws Exception {
this.mockMvc = webAppContextSetup(webApplicationContext).build();
}
#Test
public void naturezaTituloNotFound() throws Exception {
mockMvc.perform(get("/naturezatitulo/55ce2dd6222e629f4b8d6fe0"))
.andExpect(status().is4xxClientError());
}
#Test
public void naturezaTituloSave() throws Exception {
NaturezaTitulo naturezaTitulo = new NaturezaTitulo();
naturezaTitulo.setNatureza("Testando");
mockMvc.perform(put("/naturezatitulo/").content(this.json(naturezaTitulo))
.contentType(contentType))
.andExpect(jsonPath("$.id", notNullValue()));
}
protected String json(Object o) throws IOException {
MockHttpOutputMessage mockHttpOutputMessage = new MockHttpOutputMessage();
this.mappingJackson2HttpMessageConverter.write(
o, MediaType.APPLICATION_JSON, mockHttpOutputMessage);
return mockHttpOutputMessage.getBodyAsString();
}
}
but I got this error:
java.lang.IllegalArgumentException: json can not be null or empty at
com.jayway.jsonpath.internal.Utils.notEmpty(Utils.java:259)
how can I pass one object from body in Put test?
tks