Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide TestContext support for @Configuration classes [SPR-6184] #10852

Closed
spring-issuemaster opened this issue Nov 5, 2008 · 16 comments
Closed

Comments

@spring-issuemaster
Copy link
Collaborator

@spring-issuemaster spring-issuemaster commented Nov 5, 2008

Chris Beams opened SPR-6184 and commented

Currently, JavaConfig integrates with the TestContext framework via its ContextLoader implementation, JavaConfigContextLoader:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="com.bank.TransferAppConfig",
                      loader=JavaConfigContextLoader.class)
public class TransferServiceTests {
    // @Test methods ...
}

This integration works well enough but is not refactoring-friendly due to the requirement that @ContextConfiguration imposes that locations must be a String[]. It forces the fully-qualified class name to be expressed as a string, when it would clearly be superior to be able to pass it around by Class literal.

Implementing this change would require a change to core, either by adding an attribute to @ContextConfiguration that takes a Class[] (not likely), or otherwise devising a way of proving a customized annotation (e.g., @JavaConfigContextConfiguration).


Affects: 3.0 GA

Issue Links:

  • #13033 Introduce SmartContextLoader SPI ("is depended on by")
  • #12888 Document TestContext support for @Configuration classes in the reference manual ("is depended on by")
  • #12425 SpringJUnit4ClassRunner is not compatible with java context configuration ("is duplicated by")
  • #11233 Annotation configuration based TestContext ("is duplicated by")
  • SJC-272 JavaConfigContextLoader support with Spring 3.0 ("is duplicated by")
  • #13042 AnnotationConfigContextLoader should not restrict the name of default configuration classes to ContextConfiguration
  • #12615 TestContext framework should support declarative configuration of bean definition profiles
  • #12592 Support for dependency injection and initialization of test instances using classpath scanning
  • #10353 Provide dedicated ApplicationContext implementations for use with (JavaConfig) @Configuration classes
  • #12078 Support Java-Based Application Configuration

37 votes, 30 watchers

@spring-issuemaster
Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Nov 27, 2008

James Strachan commented

a new annotation might be cleaner which would also involve not having to specify the loader parameter too.

e.g.

@RunWith(SpringJUnit4ClassRunner.class) 
@JavaContextConfiguration(config=TransferAppConfig.class) 
public class TransferServiceTests { 
    // @Test methods ... 
} 

You could also introduce the convention over configuration approach used in Spring Testing to default to looking for a nested ContextConfig class maybe (rather like how Spring Test looks for ${className}-context.xml.

e.g.

@RunWith(SpringJUnit4ClassRunner.class) 
@JavaContextConfiguration
public class TransferServiceTests { 
    // @Test methods ... 


  @Configuration
  public static class ContextConfig {
    ....
  }
} 
@spring-issuemaster
Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Nov 28, 2008

Chris Beams commented

Thanks James. Similar ideas have been floating around internally, and while the dedicated annotation approach will require some changes to the TestContext framework in core, we'll see if we can work that in during the 3.0 timeline.

  • C
@spring-issuemaster
Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Mar 22, 2010

David J. M. Karlsen commented

Is it possible to get this into 3.0.2?
I cannot find JavaConfigContextLoader as part of 3.0.1?

@spring-issuemaster
Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jun 1, 2011

Sam Brannen commented

Introduced AnnotationConfigContextLoader which can be used as seen in the following examples from the Spring test suite.

@Configuration
public class DefaultConfigClassesBaseTestsConfig {

	@Bean
	public Employee employee() {
		Employee employee = new Employee();
		employee.setName("John Smith");
		employee.setAge(42);
		employee.setCompany("Acme Widgets, Inc.");
		return employee;
	}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
public class DefaultConfigClassesBaseTests {

	@Autowired
	protected Employee employee;

	@Test
	public void verifyEmployeeSetFromBaseContextConfig() {
		assertNotNull("The employee field should have been autowired.", this.employee);
		assertEquals("John Smith", this.employee.getName());
	}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class, classes = DefaultConfigClassesBaseTestsConfig.class)
public class ExplicitConfigClassesBaseTests {

	@Autowired
	protected Employee employee;

	@Test
	public void verifyEmployeeSetFromBaseContextConfig() {
		assertNotNull("The employee should have been autowired.", this.employee);
		assertEquals("John Smith", this.employee.getName());
	}
}

Notes:

  • DefaultConfigClassesBaseTests does not explicitly declare what @Configuration classes to use in @ContextConfiguration. Consequently a default configuration class name will be generated by appending "Config" to the name of the test class, resulting in DefaultConfigClassesBaseTestsConfig.
  • ExplicitConfigClassesBaseTests explicitly declares what @Configuration classes to use in @ContextConfiguration via the new classes attribute.
  • As with XML resource locations, the following are also supported with configuration classes via @ContextConfiguration:
    • configuration inheritance
    • merging local with inherited configuration
    • overriding inherited configuration (via the inheritLocations attribute)
@spring-issuemaster
Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jun 1, 2011

Eric Sirianni commented

Cool. IMO it would also be nice for the 'convention-over-configuration' default lookup to alternatively look for a nested static inner class of the annotated TestCase with an @Configuration annotation. We wrote our own custom JUnit runner that follows this approach and it works quite nicely. This way you don't have to pollute the top-level class namespace with XxxTestConfig classes for each XxxTest class.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
public class MyTest {

  @Configuration
  static class MyConfiguration {
    @Bean public Employee employee() { ... }
  }
 
  @Autowired
  Employee employee;
}
@spring-issuemaster
Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jun 1, 2011

Chris Beams commented

Eric - this is worth a separate issue; it's something we've discussed internally as well and it's nice to know that users do indeed want it. Would you please transcribe the above to a new feature request? Thanks.

[Edited] Nevermind, actually: #12615 already exists.

@spring-issuemaster
Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jun 3, 2011

Sam Brannen commented

Hi Eric,

Note that you can extend AnnotationConfigContextLoader and override its getResourceSuffix() method to return whatever you like.

The default implementation looks like this:

protected String getResourceSuffix() {
    return "Config";
}

In your custom extension of AnnotationConfigContextLoader you could override getResourceSuffix() like this (note the dollar sign $):

protected String getResourceSuffix() {
    return "$Config";
}

Then a nested static class called Config would be used as the default @Configuration class.

Regards,

Sam

p.s. Of course, if more developers prefer $Config over Config as the default resource suffix, we could switch the default to $Config.

@spring-issuemaster
Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jun 3, 2011

Chris Beams commented

Linking to #12615, which actually already requests supporting nested $Config classes as the default.

@spring-issuemaster
Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jun 3, 2011

Sam Brannen commented

Chris, I think you're confused... #12615 deals with bean definition profiles, not @Configuration classes.

In fact the use of a nested static @Configuration class was proposed in comments for this issue. ;)

So we would still need a separate issue to address the proposals from James and Eric.

@spring-issuemaster
Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jun 3, 2011

Dave Syer commented

I like the "$*" prefix as a default. I would probably prefer something more descriptive in that case (e.g. "$ContextConfiguration").

@spring-issuemaster
Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jun 3, 2011

Eric Sirianni commented

I agree with Dave. The ideal flexibility would be to support wildcards with the semantics being that any class that matches that wildcard AND has an @Configuration annotation could be picked up. And further, to default the pattern to "$*" -- this would satisfy the 'convention over configuration' but also give some naming flexibility to users.

I can file another bug, but it seems like changing the default is a binary decision rather than an enhancement on top of this feature request. So why not just do that as part of the initial checkin of this feature?

@spring-issuemaster
Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jun 3, 2011

Sam Brannen commented

Hi guys,

Thanks for the quick feedback!

I've modified AnnotationConfigContextLoader so that it now defines "$ContextConfiguration" as the resource suffix for the generated default @Configuration class name.

Thus my previous examples are amended as follows:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
public class DefaultConfigClassesBaseTests {

	@Configuration
	static class ContextConfiguration {

		@Bean
		public Employee employee() {
			Employee employee = new Employee();
			employee.setName("John Smith");
			employee.setAge(42);
			employee.setCompany("Acme Widgets, Inc.");
			return employee;
		}
	}


	@Autowired
	protected Employee employee;


	@Test
	public void verifyEmployeeSetFromBaseContextConfig() {
		assertNotNull("The employee field should have been autowired.", this.employee);
		assertEquals("John Smith", this.employee.getName());
	}

}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class, classes = DefaultConfigClassesBaseTests.ContextConfiguration.class)
public class ExplicitConfigClassesBaseTests {

	@Autowired
	protected Employee employee;


	@Test
	public void verifyEmployeeSetFromBaseContextConfig() {
		assertNotNull("The employee should have been autowired.", this.employee);
		assertEquals("John Smith", this.employee.getName());
	}

}

Notes:

  • DefaultConfigClassesBaseTests does not explicitly declare what @Configuration classes to use in @ContextConfiguration. Consequently a default configuration class name will be generated by appending "$ContextConfiguration" to the name of the test class, resulting in DefaultConfigClassesBaseTests$ContextConfiguration. This is a static class nested within the test class.
  • ExplicitConfigClassesBaseTests explicitly declares what @Configuration classes to use in @ContextConfiguration via the new classes attribute.
  • As with XML resource locations, the following are also supported with configuration classes via @ContextConfiguration:
    • configuration inheritance
    • merging local with inherited configuration
    • overriding inherited configuration (via the inheritLocations attribute)
@spring-issuemaster
Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jun 3, 2011

Sam Brannen commented

Eric,

I agree with Dave. The ideal flexibility would be to support wildcards with the semantics being that any class that matches that wildcard AND has an @Configuration annotation could be picked up. And further, to default the pattern to "$*" – this would satisfy the 'convention over configuration' but also give some naming flexibility to users.

I can file another bug, but it seems like changing the default is a binary decision rather than an enhancement on top of this feature request. So why not just do that as part of the initial checkin of this feature?

What you describe would certainly be very flexible; however, that change will not make it into the soon-to-be-released Spring 3.1 M2.

For 3.1 M2 we will stick with the current static "$ContextConfiguration" default suffix.

So please do create a new JIRA issue to request the dynamic "$*" behavior you've suggested and select 3.1 RC1 as the fix version.

Thanks,

Sam

@spring-issuemaster
Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jun 19, 2011

Sam Brannen commented

Please note that, as a result of the changes implemented in conjunction with #13042, AnnotationConfigContextLoader now generates a list of default configuration classes by finding all non-private, non-final, static, inner classes of the test class that are annotated with @Configuration. This lifts the restriction that such candidate default configuration classes be named ContextConfiguration. In other words, developers now have the freedom to name static inner @Configuration classes as they wish, and such classes will be automatically recognized as default configuration classes.

@spring-issuemaster
Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jun 19, 2011

Liam Knox commented

I think you need to be simple here.

You already have a fantastic assumption of test contexts end with -context. You should close the loop here. It can be an inner class or outer, you then have a consistent framework

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
2 participants
You can’t perform that action at this time.