Spring #ContextHierarchy in a multilevel test class hierarchy (using Scala and ScalaTest) - spring

Instead of having several full blown applications contexts, copy&pasting 99% of the content and adding/removing only a few lines where the context should differ, I'd rather have all the generic stuff in one parent context i.e. "rootContext.xml". Everything else would be part of the child context and either be merged into the parent context or replace it.
[1] describes all you need to set up a #ContextHierarchy and use a unit test class hierarchy. But it isn't working.
My class hierarchy first:
#RunWith(classOf[JUnitRunner])
#ContextHierarchy(value = Array(new ContextConfiguration(locations = Array("classpath:/rootTestContext.xml"), name = "root")))
class TestBase extends FunSuite {...}
#ContextHierarchy(value = Array(new ContextConfiguration(locations = Array("classpath:/persistenceTestContext.xml"), name = "root", ???inheritLocations = false???)))
class PersistenceTestBase extends TestBase {...}
class BasicTest extends TestBase {
test("blah") { ... }
}
class NeedsExtraContextForPersistenceTest extends PersistenceTestBase {
test("persist!") { ... }
}
Spring application context files:
rootTestContext.xml:
<import resource="classpath:/configfiles/spring/genericCaching.xml"/>
<context:annotation-config/>
<context:component-scan base-package="my.package.cm" />
<context:component-scan base-package="my.package.package2.cm" />
persistenceTestContext.xml:
<import resource="classpath:/configfiles/spring/persistence.xml"/>
So persistenceTestContext.xml should add one line to the parentContext. The generic stuff that used to be there is now in rootTestContect.xml. However when running NeedsExtraContextForPersistenceTest spring's component scanner isn't picking up spring beans for autowiring. It seems as if the scanner as defined in the parent context has no effect. Just to make this clear, there is no issue running the test when using the standard way (via #ContextConfiguration) and a complete persistenceTestContext.xml (that includes the scanner config)
Since there are only a few examples using test hierarchies with #ContextHierarchy and none using Scala + ScalaTest I'd be glad if you could provide some insight.
[1] http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#testcontext-ctx-management-ctx-hierarchies

Those annotations are processed by the Spring JUnit runner, #RunWith(SpringJUnit4ClassRunner.class); if you write standard junit tests (in scala) and use that runner then it should work. If you want to use scalatest with this spring stuff you'll perhaps need to perhaps write your own runner that invokes both the things the spring runner would do and the things that the scalatest runner would do.

Related

how to pre-populate spring properties from tests

I've a slight race condition when it comes to loading spring properties for an integration test using #TestPropertySource.
Consider the following;
test (using Spock but same for JUnit)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#TestPropertySource(locations = "classpath:test/simple-test.properties")
class SimpleStuff extends Specification {
public static final String inputDirectoryLocation = "/tmp/input-test-folder"
def "test method"() {
//do test stuff
}
}
simple-test.properties
inputDirectoryLocation=/tmp/input-test-folder
Spring Component
#Component
class SpringComponent {
#Value('${inputDirectoryLocation}')
String inputDirectory;
//do other stuff
}
The above works fine but how would I make the test fully isolated and NOT have a dependency on the FileSystem having the folder /tmp/input-test-folder (as not all users running this test are allowed to create a /tmp folder on their FS)
For example, I would like to use something like
inputDirectoryLocation = Files.createTempDirectory()
so that
#Value('${inputDirectoryLocation}')
String inputDirectory;//equals the output of Files.createTempDirectory()
resulting in test using the OS default temporary folder location & allows us to have the test simply delete the temp folder on cleanup. Is there an eloquent solution to solve the above?
Note: using Spring boot 1.5
Turned out simple enough - simply had to change the value in the properties file to refer to the
inputDirectoryLocation=${java.io.tmpdir}/input-test-folder
Then have my Spock specification create the temp folder prior to launching Spring (by using the setup() fixture method )

Spring profiles on integration tests class

we have selenium tests which are ran by java test class.
On local environment everything is ok, but I want to switch off those tests when run on jenkins.
So I use:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebIntegrationTest("server.port=1234")
#Profile("!jenkins")
#ActiveProfiles("integrationtests")
public class LoginAndEditProfileSeleniumTest {
...
What works:
running mvn clean test run all tests locally, with integrationtests profile active. I dont want to pass any additional parameter.
What I want to achieve:
running mvn clean test -Dspring.profiles.active=jenkins switch off this test.
Can I merge somehow profile passed by parameter, ActiveProfile annotation and take Profile annotation into consideration? :)
//update:
Its possible to use class extending ActiveProfilesResolver:
public class ActiveProfileResolver implements ActiveProfilesResolver {
#Override
public String[] resolve(Class<?> testClass) {
final String profileFromConsole = System.getProperty("spring.profiles.active");
List<String> activeProfiles = new ArrayList<>();
activeProfiles.add("integrationtests");
if("jenkins".contains(profileFromConsole)){
activeProfiles.add("jenkins");
}
return activeProfiles.toArray(new String[activeProfiles.size()]);
}
}
but it seems to not to cooperate with #Profile anyway ( jenkins profile is active but test is still running ) .
#Profile has zero affect on test classes. Thus, you should simply remove that annotation.
If you want to enable a test class only if a given system property is present with a specific value, you could use #IfProfileValue.
However, in your scenario, you want to disable a test class if a given system property is present with a specific value (i.e., if spring.profiles.active contains jenkins).
Instead of implementing a custom ActiveProfileResolver, a more elegant solution would be to use a JUnit assumption to cause the entire test class to be ignored if the assumption fails.
This should work nicely for you:
import static org.junit.Assume.*;
// ...
#BeforeClass
public static void disableTestsOnCiServer() {
String profilesFromConsole = System.getProperty("spring.profiles.active", "");
assumeFalse(profilesFromConsole.contains("jenkins"));
}
Regards,
Sam (author of the Spring TestContext Framework)

NullpointerException: cannot get property on null object

Working on IDEA and trying to launch the following code:
package com.myCompany.routing.spring
import com.dropbox.core.DbxRequestConfig
import grails.util.Holders
import spock.lang.Specification
class DropboxSpringConfigSpec extends Specification {
def grailsApplication=Holders.grailsApplication
def "It instantiates and configures the dropboxRequestConfig component"() {
given:
def ctx = grailsApplication.mainContext
//do stuff...
}
}
I get the following error:
java.lang.NullPointerException: Cannot get property 'mainContext' on null object
at com.myCompany.routing.spring.DropboxSpringConfigSpec.It instantiates and configures the dropboxRequestConfig component(DropboxSpringConfigSpec.groovy:20)
I've recently made a pull on my VCS, so the code should work.
When running the test as Grails test, I get the following error:
Error |
2015-03-04 13:32:00,989 [localhost-startStop-1] ERROR context.GrailsContextLoader - Error initializing the application:
Missing configuration in Config.groovy: connection.uri.
Okay, it seems some configurations in Config.groovy were given the values of some environment variables:
elasticSearch {
connection {
uri = env.ES_URL
username = env.ES_USER
password = env.ES_PASSWORD
}
indexPrefix = 'test-'
}
Since I never created the corresponding environment variables, the GrailsContextLoader fails to find the corresponding value and the computation fail.
Initializing the required environmnent variables in my IDE and running the tests as Grails tests solved the problem.
By your extends Specification seems that what you have there is a unit test. grailsApplication is not available on unit specs, though it can be mocked (ie. using the #TestFor(ClassUnderTest) annotation mockes it up for you).
If what you want to test is configuration I would recommend writing an integration spec. On the integration phase, you basically have a wired grails application without the web interface. In that case all you'll need would be to do is
package com.myCompany.routing.spring
import com.dropbox.core.DbxRequestConfig
import grails.util.Holders
import grails.test.spock.IntegrationSpec
class DropboxSpringConfigSpec extends IntegrationSpec {
def grailsApplication //This will be auto-wired
def "It instantiates and configures the dropboxRequestConfig component"() {
given:
def ctx = grailsApplication.mainContext
//do stuff...
}
}
Re that test having worked previously in your VCS. I've never used Holders on a unit spec, so I cannot really say it wouldn't work, but it may have just been a false positive. From my understanding on the unit phase you don't have a running grails application and beans (including config) would not be available until mocked by you or the testing framework (again, using #TestFor or #Mock)

Retrieve and Modify the #ConfigurationContext programmatically via code?

How to Retrieve and Modify the #ConfigurationContext programmatically via code ?
I have a default configuration where it contains valid xml files.
Now i need to add an invalid configuration for a particular test case and test the same.
How to override, retrieve and modify the #ConfigurationContext programmatically via code ?
Thanks in advance,
Kathir
Disclaimer: I am assuming you are using JUnit since you didn't comment differently in your reply to my comment.
I think what you are trying to do does not make lot of sense, in my opinion it is still better to create a dedicated test class for your not-working configuration in order to be able to do more than one test. However:
annotate your test class with #RunWith(SpringJUnit4ClassRunner.class) and #ContextConfiguration(locations = {"classpath:/working-context.xml"}). In this way you can retrieve the configuration context in two ways: first, you can simply declare a field #Inject ApplicationContext context which will contain the working context. Or, you make your test class implements ApplicationContextAware and then write a public void setApplicationContext (ApplicationContext applicationContext). I would go for the second one since it will come in hand for changing the context programmatically.
write a not-working-context.xml and place it in your classpath
in the test method you want to fail, reload the application context with context = setApplicationContext(new ClassPathXmlApplicationContext("not-working-context.xml")); and test all the errors you like.
though it is not good practice to stand on test case order, make sure your failing test will be executed as the last one (tests are executing alphabetically) so you don't have to reload the working context in the other tests.
In the end your test class will look like:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:/working-context.xml"})
public class TestClass implements ApplicationContextAware {
private ApplicationContext context;
public void setApplicationContext(ApplicationContext context){
this.context = context;
}
//Other tests
#Test
public void zFailingTest() {
context = setApplicationContext(new ClassPathXmlApplicationContext("not-working-context.xml"));
//your test
}
}

Grails Dependency Injection Outside of Services?

I have a Grails application that needs to run a strategy that will likely be swapped out over time. I know Spring underlies Grails, so I was wondering if I had access to Spring's IoC container so that I could externalize the actual dependency in an xml file (note: I have never actually done this, but just know of it, so I may be missing something). My goal is to be able to do something like the following:
class SchemaUpdateService {
public int calculateSomething(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
IStrategy strat = (IStrategy) ctx.getBean("mystrat");
}
}
And then map the appropriate implementation in the beans.xml file. I assume this is supported in Grails. Does anyone have any documentation on how this would work? Do I really just need the Spring IoC library and it will just work? Thanks!
You define your beans in resources.xml or resources.groovy. The grails documentation is very clear about how to access the Spring application context.
You can access the application context from any Grails artefact using
ApplicationContext ctx = grailsApplication.mainContext
You can then use this to retrieve whichever beans you're interested in:
IStrategy strat = (IStrategy) ctx.getBean("mystrat")
In classes that don't have access to grailsApplication, you could use a helper such as the following to access the application context and the beans therein
class SpringUtils {
static getBean(String name) {
applicationContext.getBean(name)
}
static <T> T getBean(String name, Class<T> requiredType) {
applicationContext.getBean(name, requiredType)
}
static ApplicationContext getApplicationContext() {
ApplicationHolder.application.mainContext
}
}
However, this should only be necessary if you need to retrieve different implementations of the same bean at runtime. If the required bean is known at compile-time, just wire the beans together in resources.xml or resources.groovy
First of all, you want to define your strategy in your grails-app/conf/spring/resources.groovy:
beans = {
myStrat(com.yourcompany.StrategyImpl) {
someProperty = someValue
}
}
Then, you simply def the a property with the same name into your service:
class SomeGrailsService {
def myStrat
def someMethod() {
return myStrat.doSomething()
}
}
In any Grails artefact (such as services and domain classes), Grails will automatically give the myStrat property the correct value. But don't forget, in a unit test you'll have to give it a value manually as the auto-wiring does not happen in unit tests.
Outside of a Grails artefact, you can use something like:
def myStrat = ApplicationHolder.application.mainContext.myStrat
In Grails 2.0, Graeme et al are deprecating the use of the *Holder classes (such as ApplicationHolder and ConfigurationHolder), so I'm not quite sure what the Grails 2.0 approach would be...

Resources