Create a TestServer with an already started FakeApplication - java

I've started integrating in some Selenium tests into the testing framework in Play. I have a class in which I define a lot of special config settings for a FakeApplication, then create that FakeApplication using:
public abstract class FakeApplicationTest {
public static FakeApplication createFakeApp() {
// grab the main application.conf file that is used to start up the Play application
Config config = ConfigFactory.parseFile(new File("conf/application.conf"));
// resolve all variables within this config file with other variables within itself
config = ConfigFactory.load(config);
// create a Configuration object out of this and then turn it into a Map to be modified
Configuration configuration = new Configuration(config);
Map<String, Object> fakeApplicationConf = Maps.newHashMap(configuration.asMap());
...
// CUSTOM CONFIG THINGS HERE
...
return Helpers.fakeApplication(fakeApplicationConf);
}
}
What I would like to be able to do, is use this FakeApplication, start it in a #Before method (using JUnit 4), and then pass that already running FakeApplication to the TestServer which is needed to run the Selenium tests.
public class Checkout extends FluentTest {
public WebDriver webDriver = new FirefoxDriver();
...
public FakeApplication app;
#Before
public void beforeTest() {
app = FakeApplicationTest.createFakeApp();
Helpers.start(app);
FakeApplicationTest.createCleanDb();
...
}
#Test
public void testReviewPage()
running(testServer(3333, app), webDriver, browser -> {
...
}
}
}
What seems to happen when I do this though, is that the existing, running FakeApplication gets ignored/tossed and a new FakeApplication is created and started which is not setup with my custom fakeApplicationConf Map... or, it's stopping the app and restarting it but goes back to only use my default application.conf.
Any ideas on why this is? or if I can somehow accomplish this in a different way?

Related

Could not find an 'annotation declaring class' for unit tests

I am setting up an Spring boot application on Jenkins. For the unit tests i am getting below error. This error is not particular to one test cases. Every time I run it is giving me error for different test. I am not sure what is wrong. Same project is working fine (build and unit tests) on local and other environments like (development, stage). Any idea with below errors?
00:49:42.836 [main] DEBUG org.springframework.test.context.support.ActiveProfilesUtils - Could not find an 'annotation declaring class' for annotation type [org.springframework.test.context.ActiveProfiles] and class [com.abc.services.tokens.crypto.aws.AesGcmDynamoCryptoCipherProviderTest]
00:49:42.836 [main] INFO org.springframework.test.context.support.DefaultTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.web.ServletTestExecutionListener#43195e57, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener#333291e3, org.springframework.test.context.support.DependencyInjectionTestExecutionListener#479d31f3, org.springframework.test.context.support.DirtiesContextTestExecutionListener#40ef3420]
Here is the test class
#SuppressWarnings("unchecked")
public class AesGcmDynamoCryptoCipherProviderTest extends AbstractTestNGBeanMockingTests {
#MockBean
AwsCrypto awsCrypto;
#MockBean
DynamoDBProvider dynamoDBProvider;
#MockBean
MasterKeyProvider masterKeyProvider;
#MockBean
Table table;
private static Item mockCipherItem(UUID cipherId) {
Item item = mock(Item.class);
return item;
}
private static <T> CryptoResult<T, ?> mockCryptoResult(T result) {
// do something
return cryptoResult;
}
#BeforeMethod
private void init() {
CryptoResult<String, ?> decryptoResult = mockCryptoResult(Base64.getEncoder().encodeToString("*decrypted*".getBytes()));
CryptoResult<String, ?> encryptoResult = mockCryptoResult("*encrypted*");
}
#Test
public void testGetCipher() {
AesGcmDynamoCryptoCipherProvider provider = new AesGcmDynamoCryptoCipherProvider("table", awsCrypto, dynamoDBProvider, masterKeyProvider);
UUID cipherId = UUID.randomUUID();
Item cipherItem = mockCipherItem(cipherId);
AesGcmCipher cipher = provider.getCipher(cipherId);
assertNotNull(cipher);
assertEquals(cipher.getCipherId(), cipherId);
}
}
Base class
#ContextConfiguration(classes = { //...
AbstractTestNGBeanMockingTests.MockBeanConfiguration.class //...
})
#DirtiesContext
public class AbstractTestNGBeanMockingTests extends AbstractTestNGSpringContextTests {
private static ThreadLocal<Class<? extends AbstractTestNGBeanMockingTests>> currentTestClass = new ThreadLocal<>();
#AfterClass(alwaysRun = true)
#Override
protected void springTestContextAfterTestClass() throws Exception {
super.springTestContextAfterTestClass();
}
#BeforeClass(alwaysRun = true, dependsOnMethods = { "springTestContextBeforeTestClass" })
#Override
protected void springTestContextPrepareTestInstance() throws Exception {
currentTestClass.set(this.getClass());
super.springTestContextPrepareTestInstance();
currentTestClass.set(null);
}
#BeforeMethod
public void initializeMockedBeans() {
MockBeanRegistration.initializeMockedBeans(this);
}
protected static class MockBeanConfiguration {
MockBeanConfiguration(ApplicationContext context) {
MockBeanRegistration.registerMocks((BeanDefinitionRegistry) context, currentTestClass.get());
}
}
}
I have bumped into this error after moving classes into new packages somewhere under the java folder, but omitting to move the corresponding test classes in the test folders.
After applying the changes in the test packages as well, it runs again.
You wrote that you experience the problem only in the Jenkins environment.
My guess is that Jenkins starts always with a new checkout of the project from a 100% clean status. In the other environments you might have some residues from the previous development, and these somehow allow the tests to 'work', but I would expect that it is Jenkins getting it right...
Try to setup the app in a development environment from scratch. If you get the error, so you will properly analyze it and correct it.

Springboot, how to show Banner programmtically at start, again

If a springboot application starts, a logo/banner is shown.
I took my own, colored banner in a banner.txt file. It is shown at starts, all is fine.
But I want to repeat my banner after successfull or not successfull start as last startup message. Like: Banner + "runs" or Banner + "do not run".
Something like this:
public static void main( String[] args )
{
try {
SpringApplication.run( ControllerUndMain.class, args );
showLogo();
System.out.println('runs')
} catch(Exception e){
showLogo();
System.out.println('not working')
}
}
This helps our admins and devops to see, that startup phase ends and if applications run or not.
Question: How to show banner programmatically?
I could not found any straight way of doing it. I did a drive into spring code base and found way of doing that. I've tested in my project and its working fine.
Note: I've copied some of the class which are used by spring to print banner. I don't see any issue on reusing in our code base.
Here is a entire code....
Main class which runs springboot application and I've created method to print banner.
public class DemoApplication {
#Autowired
private Environment env;
public static void main(String[] args) {
SpringApplication app = new SpringApplication(DemoApplication.class);
ConfigurableApplicationContext test = app.run(args);
DemoApplication application = new DemoApplication();
application.printBanner(app, test);
}
public void printBanner(SpringApplication app, ConfigurableApplicationContext test) {
ResourceLoader resourceLoader = (app.getResourceLoader() != null ? app.getResourceLoader()
: new DefaultResourceLoader(app.getClassLoader()));
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, null);
Banner banner = bannerPrinter.print(DemoApplication.class, test.getEnvironment());
banner.printBanner(test.getEnvironment(), DemoApplication.class, System.out);
}
}
After adding above code base just copy SpringApplicationBannerPrinter and SpringBootBanner class(You will get those form Spring code base) in your project and run.
Note: 1) I've tested before posing answer here.
2) To make answer short, I've not pasted SpringApplicationBannerPrinter and SpringBootBanner. Let me know if you want me to paste those class in answer
#SpringBootApplication
public class SimpleDemoApplication {
public static void main(String[] args) {
final SpringApplication app;
final ConfigurableApplicationContext context;
app = new SpringApplication(SimpleDemoApplication.class);
context = app.run(args);
print(context);
}
public static void print(ConfigurableApplicationContext context) {
Banner banner = context.getBean(Banner.class);
banner.printBanner(context.getEnvironment(), SimpleDemoApplication.class, System.out);
}
}
Just inject or get over context.getBean the org.springframework.boot.Banner.class. This helps for the good case, if all is fine and context is up.
I dont have a solution for the bad case, if context dont running.

#SpringBootTest + #BeforeAll

I have a small spring boot app with database and rabbitmq usages.
So I would like to test with integration test (H2 + apache qpid).
#ExtendWith(SpringExtension.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = TestSpringConfig.class)
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
As my app expect database and mq Im using #BeforeAll to start it:
#BeforeAll
public void before() {
startMessageBroker();
startDatabase();
}
The problem is that my web app starts before database/mq defined in #BeforeAll.
org.springframework.test.context.junit.jupiter.SpringExtension:
public class SpringExtension implements BeforeAllCallback, AfterAllCallback, TestInstancePostProcessor,
BeforeEachCallback, AfterEachCallback, BeforeTestExecutionCallback, AfterTestExecutionCallback,
ParameterResolver {
// ...
#Override
public void beforeAll(ExtensionContext context) throws Exception {
getTestContextManager(context).beforeTestClass();
}
// ...
#Override
public void postProcessTestInstance(Object testInstance, ExtensionContext context) throws Exception {
getTestContextManager(context).prepareTestInstance(testInstance);
}
// ...
Web app starts in postProcessTestInstance phase and #BeforeAll methods in beforeAll.
org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor:
private void execute(TestDescriptor testDescriptor, C parentContext, ExecutionTracker tracker) {
Node<C> node = asNode(testDescriptor);
tracker.markExecuted(testDescriptor);
C preparedContext;
try {
preparedContext = node.prepare(parentContext); // 1 <<<
SkipResult skipResult = node.shouldBeSkipped(preparedContext);
if (skipResult.isSkipped()) {
this.listener.executionSkipped(testDescriptor, skipResult.getReason().orElse("<unknown>"));
return;
}
}
catch (Throwable throwable) {
rethrowIfBlacklisted(throwable);
// We call executionStarted first to comply with the contract of EngineExecutionListener
this.listener.executionStarted(testDescriptor);
this.listener.executionFinished(testDescriptor, TestExecutionResult.failed(throwable));
return;
}
this.listener.executionStarted(testDescriptor);
TestExecutionResult result = singleTestExecutor.executeSafely(() -> {
C context = preparedContext;
try {
context = node.before(context); // 2 <<<
C contextForDynamicChildren = context;
context = node.execute(context, dynamicTestDescriptor -> {
this.listener.dynamicTestRegistered(dynamicTestDescriptor);
execute(dynamicTestDescriptor, contextForDynamicChildren, tracker);
});
C contextForStaticChildren = context;
// #formatter:off
testDescriptor.getChildren().stream()
.filter(child -> !tracker.wasAlreadyExecuted(child))
.forEach(child -> execute(child, contextForStaticChildren, tracker));
// #formatter:on
}
finally {
node.after(context);
}
});
this.listener.executionFinished(testDescriptor, result);
}
See points 1 and 2. There are executions of 'prepare' and then 'before'.
Im not sure is it issue of junit, SpringExtension or Im doing something wrong.
Any advice?
junit-jupiter: 5.0.1
spring-test: 5.0.0.RELEASE
spring-boot-test: 1.5.8.RELEASE
Checkout https://www.testcontainers.org/ it provides integration with JUnit to launch RabbitMQ and a database in docker containers as part of the JUnit testing. This makes integration tests much realistic because you using the same versions of database and message queue would be using in production.
This is by design, I think. Try to add the Bean post-processor/Context initializer to init/start your DB/rabbitMQ..
Is there any reason to start the DB and the message broker in test class? It seems to me that this is wrong by design. They both should be started along with your application context since they are part of your infrastructure.
It's not a responsibility of your tests to set up your infrastructure!
IMHO, a better way of doing things is the following:
Use H2 dependency with a test scope in maven + configure a starter in a way it starts H2 when the application context is starting
Start apache qpid (preferably embedded) on application start
In #Before just make sure you clean up stuff before running a test case
JUnit 5 [#BeforeAll] annotation is replacement of #BeforeClass annotation in JUnit 4.
It is used to signal that the annotated method should be executed before all tests in the current test class.
#BeforeAll should be used in static method
For More reading:
http://junit.org/junit5/docs/current/user-guide/#writing-tests-annotations

Is there any way to test classes that access ContentResolver with JUnit? Or do I have to use Robolectric?

I have a test class like this:
public class PostRepositoryTest extends AndroidTestCase {
Context context;
#Before
public void setUp() throws Exception {
context = new Application();
}
#Test
public void testFindAll() {
PostRepository postRepository = PostRepository.get(context);
List<Post> all = postRepository.findAll();
assertEquals(0, all.size());
}
}
findAll method just uses a ContentResolver to return results from a ContentResolver.
public List<Post> findAll() {
Cursor c = new PostSelection().query(context.getContentResolver());
return listFromCursor(c);
}
Before trying this way... I was doing this by using AndroidInstrumentationTestCase2 and launching an activity to check all of those things, but I want to avoid that.
I'd like to do this as an unit test. Is it possible?
It depends whether you want to run this test case on a device or not. Go for Robolectric if you want to run it on local JVM instead of running on device.
If you want to run on device without AndroidInstrumentationTestCase2, then you can use AndroidJUnitRunner. Its based on Junit4 specification.
Read the link here:
https://developer.android.com/training/testing/unit-testing/instrumented-unit-tests.html
See the code samples for basic idea:
https://github.com/googlesamples/android-testing/tree/master/unittesting/BasicUnitAndroidTest
If you setup your test suite according to the above link then you don't need to extend any class or launch an activity. You can get context object using the code:
InstrumentationRegistry.getTargetContext();
or
InstrumentationRegistry.getContext();

How distinguish from which test suite was junit test run in case the same tests are run more times?

I have JUnit main test suite. This suite contains many suites - for each testing configuration
#RunWith(ProgressSuite.class)
#SuiteClasses({
SimpleTest.class,
AboutTest.class,
CDH4_JDBC_TestSuite.class,
CDH5_JDBC_TestSuite.class,
CDH4_Metastore_TestSuite.class,
CDH5_Metastore_TestSuite.class,
CDH4_JDBC_Kerberos_TestSuite.class,
CDH5_JDBC_Kerberos_TestSuite.class,
CDH4_Metastore_Kerberos_TestSuite.class,
CDH5_Metastore_Kerberos_TestSuite.class,
})
public class TestSuite {
}
Suites for each testing configuration contains the same test cases, but contains different setUpClass() and tearDownClass() methods
#RunWith(Suite.class)
#SuiteClasses({
PerspectiveSwitchTest.class,
NewFolderFromToolbarTest.class,
RenameFolderFromToolbarTest.class,
RenameFileFromToolbarTest.class,
OpenFilePropertiesFromToolbarTest.class,
OpenFolderPropertiesFromToolbarTest.class,
DeleteFileFromToolbarTest.class,
DeleteFolderFromToolbarTest.class,
CopyPasteFolderFromToolbarTest.class,
CopyPasteFileFromToolbarTest.class,
CutPasteFolderFromToolbarTest.class,
CutPasteFileFromToolbarTest.class,
})
public class CDH4_JDBC_Kerberos_TestSuite {
private static SWTWorkbenchBot bot = new SWTWorkbenchBot();
private static AddNewEcosystemNavigator addNewEcosystemNavigator;
private static EcosystemConfigurationLoader ecosystemConfigurationLoader;
private static EcosystemConfiguration ecosystemConfiguration;
private static GenericNavigator genericNavigator;
#BeforeClass
public static void setUpClass() {
bot = new SWTWorkbenchBot();
addNewEcosystemNavigator = new AddNewEcosystemNavigator();
ecosystemConfigurationLoader = new EcosystemConfigurationLoader();
genericNavigator = new GenericNavigator();
ecosystemConfiguration = ecosystemConfigurationLoader
.getDefaultCDH4JDBCKerberosEcosystemConfiguration();
addNewEcosystemNavigator.addNewEcosystemManually(bot,
ecosystemConfiguration);
}
#AfterClass
public static void tearDownClass() {
genericNavigator.closeDialogWindow();
addNewEcosystemNavigator.discardEcosystem(bot, ecosystemConfiguration);
}
}
I am using Jenkins and Tycho for building tests. When I run test suite and some tests fails, I am not able to distinguish on which configuration tests failed. In Jekins I can see only information e.g NewFolderFromToolbarTest was runned 8 times (3 times failed, 5 times passed). Of course I am able get required information from log, but it is time consuming.
Is there any way how to get required information? e.g Use different test structure, use different jenkins plugin, renamed method dynamically if even possible etc? any ideas please? thanks a lot
You could make the test classes abstract and have each configuration be a subclass
public class CDH4NewFolderFromToolbarTest extends AbstractNewFolderFromToolbarTest{
//...
}
Then in your suite call the specific test classes
RunWith(Suite.class)
#SuiteClasses({
CDH4PerspectiveSwitchTest.class,
CDH4NewFolderFromToolbarTest.class,
CDH4RenameFolderFromToolbarTest.class,
CDH4RenameFileFromToolbarTest.class,
//...etc
})
public class CDH4_JDBC_Kerberos_TestSuite {
//same as before
}
I would advocate that instead of reconfiguring in each subclass since the #BeforeClass and #AfterClass will only be called once if it is put in the suite.

Categories