31 | * @DataSource(...)
32 | * @ContextConfiguration(Array("classpath*:/META-INF/spring/module-context.xml"))
33 | * class FooServiceTest extends org.specs2.spring.Specification {
34 | * @Autowired var service: FooService = _
35 | * @Autowired var ht: HibernateTemplate = _
36 | *
37 | * "Some such" in {
38 | * "makes many foos" ! makeFoos
39 | * }
40 | *
41 | * def makeFoos() = {
42 | * this.service.makeFoos()
43 | * this.ht.loadAll(classOf[Foo]) must have size (100)
44 | * }
45 | * }
46 | *
47 | *
48 | * @author janmachacek
49 | */
50 | public class JndiEnvironmentSetter {
51 | private final Mapnew InitialContext()
will always return a context from this factory. Use the emptyActivatedContextBuilder()
231 | * static method to get an empty context (for example, in test methods).
232 | *
233 | * @throws IllegalStateException if there's already a naming context builder registeredwith the JNDI NamingManager
234 | * @throws javax.naming.NamingException .
235 | */
236 | public void activate() throws IllegalStateException, NamingException {
237 | if (!NamingManager.hasInitialContextFactoryBuilder())
238 | NamingManager.setInitialContextFactoryBuilder(this);
239 | activated = this;
240 | }
241 |
242 | /**
243 | * Clear all bindings in this context builder.
244 | */
245 | public void clear() {
246 | for (Map.Entryjava:comp/env/bean/xyz
18 | *
19 | * @return the JNDI name
20 | */
21 | String name();
22 |
23 | /**
24 | * The type of the object; the type must have an accessible nullary constructor.
25 | *
26 | * @return the object name
27 | */
28 | Class> clazz();
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/org/specs2/spring/annotation/DataSource.java:
--------------------------------------------------------------------------------
1 | package org.specs2.spring.annotation;
2 |
3 | import java.lang.annotation.Inherited;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.sql.Driver;
7 |
8 | /**
9 | * Specifies the {@link javax.activation.DataSource} to be added to the JNDI environment
10 | *
11 | * @author janmmachacek
12 | */
13 | @Retention(RetentionPolicy.RUNTIME)
14 | @Inherited
15 | public @interface DataSource {
16 |
17 | /**
18 | * The name in the JNDI environment; typically something like java:comp/env/bean/xyz
19 | *
20 | * @return the JNDI name
21 | */
22 | String name();
23 |
24 | /**
25 | * Specifies the type of the JDBC driver
26 | *
27 | * @return the driver class
28 | */
29 | Class extends Driver> driverClass();
30 |
31 | /**
32 | * The JDBC URL
33 | *
34 | * @return the URL
35 | */
36 | String url();
37 |
38 | /**
39 | * The username to establish the DB connection
40 | *
41 | * @return the username
42 | */
43 | String username() default "sa";
44 |
45 | /**
46 | * The password to establish the DB connection
47 | *
48 | * @return the password
49 | */
50 | String password() default "";
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/org/specs2/spring/annotation/Jms.java:
--------------------------------------------------------------------------------
1 | package org.specs2.spring.annotation;
2 |
3 | /**
4 | * Specifies JMS configuration
5 | *
6 | * @author janmmachacek
7 | */
8 | public @interface Jms {
9 |
10 | /**
11 | * The name in the JNDI environment; typically something like java:comp/env/jms/connectionFactory
12 | *
13 | * @return the JNDI name
14 | */
15 | String connectionFactoryName();
16 |
17 | /**
18 | * Specifies any number of JMS queues managed by this connection factory
19 | *
20 | * @return the queues
21 | */
22 | JmsQueue[] queues() default {};
23 |
24 | /**
25 | * Specifies any number of JMS topics managed by this connection factory
26 | *
27 | * @return the topics
28 | */
29 | JmsTopic[] topics() default {};
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/org/specs2/spring/annotation/JmsQueue.java:
--------------------------------------------------------------------------------
1 | package org.specs2.spring.annotation;
2 |
3 | /**
4 | * Specifies a JMS queue at the given JNDI name
5 | *
6 | * @author janmmachacek
7 | */
8 | public @interface JmsQueue {
9 | /**
10 | * The name in the JNDI environment; typically something like java:comp/env/jms/xyz
11 | *
12 | * @return the JNDI name
13 | */
14 | String name();
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/org/specs2/spring/annotation/JmsTopic.java:
--------------------------------------------------------------------------------
1 | package org.specs2.spring.annotation;
2 |
3 | /**
4 | * Specifies a JMS topic at the given JNDI name
5 | *
6 | * @author janmmachacek
7 | */
8 | public @interface JmsTopic {
9 | /**
10 | * The name in the JNDI environment; typically something like java:comp/env/jms/xyz
11 | *
12 | * @return the JNDI name
13 | */
14 | String name();
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/org/specs2/spring/annotation/Jndi.java:
--------------------------------------------------------------------------------
1 | package org.specs2.spring.annotation;
2 |
3 | import org.specs2.spring.BlankJndiBuilder;
4 | import org.specs2.spring.JndiBuilder;
5 |
6 | import java.lang.annotation.Inherited;
7 | import java.lang.annotation.Retention;
8 | import java.lang.annotation.RetentionPolicy;
9 |
10 | /**
11 | * Use this annotation on your tests to inject values into the JNDI environment for the
12 | * tests.
13 | * You can specify any number of {@link #dataSources()}, {@link #mailSessions()}, 14 | * {@link #beans()}
15 | *In addition to these fairly standard items, you can specify {@link #builder()}, 16 | * which needs to be an implementation of the {@link org.specs2.spring.JndiBuilder} interface with 17 | * nullary constructor. The runtime will instantiate the given class and call its 18 | * {@link org.specs2.spring.JndiBuilder#build(java.util.Map)} method.
19 | * 20 | * 21 | * @author janmmachacek 22 | */ 23 | @Retention(RetentionPolicy.RUNTIME) 24 | @Inherited 25 | public @interface Jndi { 26 | 27 | /** 28 | * Specify any number of {@link javax.sql.DataSource} JNDI entries. 29 | *Typically, you'd write something like
30 | * @DataSource(name = "java:comp/env/jdbc/x", url = "jdbc:hsqldb:mem:x", username = "sa", password = "")
:
31 | * this would register a {@link javax.sql.DataSource} entry in the JNDI environment under name java:comp/env/jdbc/x
,
32 | * with the given {@code url}, {@code username} and {@code password}.
33 | *
java:comp/env/bean/xyz
16 | *
17 | * @return the JNDI name
18 | */
19 | String name();
20 |
21 | /**
22 | * The JavaMail properties for the session; for example mail.smtp.host=localhost
23 | *
24 | * @return the JavaMail properties
25 | */
26 | String[] properties() default {};
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/org/specs2/spring/annotation/Property.java:
--------------------------------------------------------------------------------
1 | package org.specs2.spring.annotation;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * @author janmachacek
10 | */
11 | @Retention(RetentionPolicy.RUNTIME)
12 | @Target(ElementType.TYPE)
13 | public @interface Property {
14 | /**
15 | * The name of the property
16 | * @return the name; never {@code null}
17 | */
18 | String name();
19 |
20 | /**
21 | * The value of the property; if you want {@code null}, use {@link org.specs2.spring.annotation.SystemEnvironment#nullValue()}
22 | * @return the value; never {@code null}
23 | */
24 | String value();
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/org/specs2/spring/annotation/SystemEnvironment.java:
--------------------------------------------------------------------------------
1 | package org.specs2.spring.annotation;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * @author janmachacek
10 | */
11 | @Retention(RetentionPolicy.RUNTIME)
12 | @Target(ElementType.TYPE)
13 | public @interface SystemEnvironment {
14 | /**
15 | * Indicates whether the properties specified entries should overwrite the existing values. When
16 | * {@code false} the existing values will be kept.
17 | * @return {@code true} if the existing values should be overwritten.
18 | */
19 | boolean overwrite() default true;
20 |
21 | /**
22 | * Indicates whether the existing environment should be cleared.
23 | * @return {@code true} if the environment should be cleared.
24 | */
25 | boolean clear() default false;
26 |
27 | /**
28 | * Value that, when used in {@link Property#value()} or in the {@code value} portion of the {@link #value()} will be interpreted
29 | * as {@code null} rather than a {@code String} with value {@code "null"}.
30 | * @return the {@code null} name.
31 | */
32 | String nullValue() default "null";
33 |
34 | /**
35 | * The properties
36 | * @return the properties
37 | */
38 | Property[] properties() default {};
39 |
40 | /**
41 | * The properties in {name1=value1, name2=value2, ..., namen=valuen}
42 | * syntax.
43 | * @return the properties.
44 | */
45 | String[] value() default {};
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/org/specs2/spring/annotation/SystemProperties.java:
--------------------------------------------------------------------------------
1 | package org.specs2.spring.annotation;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * @author janmachacek
10 | */
11 | @Retention(RetentionPolicy.RUNTIME)
12 | @Target(ElementType.TYPE)
13 | public @interface SystemProperties {
14 | /**
15 | * Indicates whether the properties specified entries should overwrite the existing values. When
16 | * {@code false} the existing values will be kept.
17 | * @return {@code true} if the existing values should be overwritten.
18 | */
19 | boolean overwrite() default true;
20 |
21 | /**
22 | * Indicates whether the existing environment should be cleared.
23 | * @return {@code true} if the environment should be cleared.
24 | */
25 | boolean clear() default false;
26 |
27 | /**
28 | * Value that, when used in {@link Property#value()} or in the {@code value} portion of the {@link #value()} will be interpreted
29 | * as {@code null} rather than a {@code String} with value {@code "null"}.
30 | * @return the {@code null} name.
31 | */
32 | String nullValue() default "null";
33 |
34 | /**
35 | * The properties
36 | * @return the properties
37 | */
38 | Property[] properties() default {};
39 |
40 | /**
41 | * The properties in {name1=value1, name2=value2, ..., namen=valuen}
42 | * syntax.
43 | * @return the properties.
44 | */
45 | String[] value() default {};
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/org/specs2/spring/annotation/TransactionManager.java:
--------------------------------------------------------------------------------
1 | package org.specs2.spring.annotation;
2 |
3 | import java.lang.annotation.Inherited;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 |
7 | /**
8 | * Specifies the {@link javax.transaction.TransactionManager} to be added to the JNDI environment
9 | *
10 | * @author janmmachacek
11 | */
12 | @Retention(RetentionPolicy.RUNTIME)
13 | @Inherited
14 | public @interface TransactionManager {
15 | /**
16 | * The name in the JNDI environment; typically something like java:comp/env/TransactionManager
17 | *
18 | * @return the JNDI name
19 | */
20 | String name();
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/org/specs2/spring/annotation/UseProfile.java:
--------------------------------------------------------------------------------
1 | package org.specs2.spring.annotation;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * @author janmachacek
10 | */
11 | @Retention(RetentionPolicy.RUNTIME)
12 | @Target(ElementType.TYPE)
13 | public @interface UseProfile {
14 |
15 | /**
16 | * Define the profiles that the test should load/use
17 | * @return the profile names
18 | */
19 | String[] value();
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/org/specs2/spring/annotation/WorkManager.java:
--------------------------------------------------------------------------------
1 | package org.specs2.spring.annotation;
2 |
3 | /**
4 | * Specifies the JNDI-bound WorkManager, either the
5 | * commonj.work.WorkManager
(when type is {@link org.specs2.spring.annotation.WorkManager.Kind#CommonJ}) or
6 | * javax.spi.resource.work.WorkManager
(when type is {@link org.specs2.spring.annotation.WorkManager.Kind#Javax}).commonj.work.WorkManager
21 | */
22 | CommonJ,
23 | /**
24 | * Create the javax.spi.resource.work.WorkManager
25 | */
26 | Javax
27 | }
28 |
29 | /**
30 | * The JNDI name for the WorkManager
31 | *
32 | * @return the name, typically "java:comp/env/work/WorkManager"
33 | */
34 | String name();
35 |
36 | /**
37 | * The kind of the WorkManager to create
38 | *
39 | * @return the type
40 | */
41 | Kind kind();
42 |
43 | /**
44 | * The minimum number of threads for the WorkManager
45 | *
46 | * @return the number of threads; always > 0.
47 | */
48 | int minimumThreads() default 2;
49 |
50 | /**
51 | * The maximum number of threads for the WorkManager.
52 | *
53 | * @return the number of threads; always > 1 and <= {@link #minimumThreads()}
54 | */
55 | int maximumThreads() default 4;
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/scala/org/specs2/spring/BeanTables.scala:
--------------------------------------------------------------------------------
1 | package org.specs2.spring
2 |
3 | import org.specs2.execute._
4 | import org.specs2.text.Trim._
5 | import org.springframework.beans.{BeanWrapperImpl, BeanWrapper}
6 | import java.util.{HashSet, ArrayList}
7 | import scala.reflect.ClassTag
8 |
9 | /**
10 | * @author janmachacek
11 | */
12 | trait BeanTables {
13 |
14 | implicit def toTableHeader(a: String) = new TableHeader(List(a))
15 |
16 | implicit def toDataRow(a: Any) = BeanRow(List(a))
17 |
18 | case class TableHeader(titles: List[String]) {
19 | def ||(title: String) = copy(titles = this.titles :+ title)
20 |
21 | def |(title: String) = copy(titles = this.titles :+ title)
22 |
23 | def |(row: BeanRow) = new AnyRefTable(titles, List(row))
24 |
25 | def |>[T1](row: BeanRow) = new AnyRefTable(titles, List(row), execute = true)
26 | }
27 |
28 | abstract class Table(val titles: List[String], val execute: Boolean = false) {
29 | /**
30 | * @return the header as a | separated string
31 | */
32 | def showTitles = titles.mkString("|", "|", "|")
33 |
34 | /**
35 | * Collect the results of each row
36 | * @param results list of (row description, row execution result)
37 | * @return an aggregated Result from a list of results
38 | */
39 | protected def collect[R <% Result](results: List[(String, R)]): DecoratedResult[BeanTable] = {
40 | val result = allSuccess(results)
41 | val header = result match {
42 | case Success(_, _) => showTitles
43 | case other => " " + showTitles
44 | }
45 | DecoratedResult(BeanTable(titles, results), result.updateMessage {
46 | header + "\n" +
47 | results.map((cur: (String, R)) => resultLine(cur._1, cur._2)).mkString("\n")
48 | })
49 | }
50 |
51 | /**@return the logical and combination of all the results */
52 | private def allSuccess[R <% Result](results: List[(String, R)]): Result = {
53 | def and(l: Result, r: (String, R)): Result = {
54 | if (l.isSuccess && r._2.isSuccess) l
55 | else if (!l.isSuccess) l
56 | else r._2
57 | }
58 | results.foldLeft(Success(""): Result)(and)
59 | }
60 |
61 | /**@return the status of the row + the values + the failure message if any */
62 | private def resultLine(desc: String, result: Result): String = {
63 | result.status + " " + desc + {
64 | result match {
65 | case Success(_, _) => ""
66 | case _ => " " + result.message
67 | }
68 | }
69 | }
70 |
71 | case class PropertyDescriptor(name: String, value: Any)
72 |
73 | }
74 |
75 | case class AnyRefTable(override val titles: List[String], rows: List[BeanRow], override val execute: Boolean = false) extends Table(titles, execute) {
76 | outer =>
77 | def |(row: BeanRow) = AnyRefTable(titles, outer.rows :+ row, execute)
78 |
79 | def |[B](f: (B) => Result)(implicit m: ClassTag[B]) = executeRow(m, f, execute)
80 |
81 | def |>[B](f: (B) => Result)(implicit m: ClassTag[B]) = executeRow(m, f, true)
82 |
83 | def |<[B](implicit m: ClassTag[B]): List[B] = {
84 | rows map {d: BeanRow => d.makeBean[B](titles, m)}
85 | }
86 |
87 | def |<[B](m: Class[B]): List[B] =
88 | rows map {d: BeanRow => d.makeBean[B](titles, m)}
89 |
90 | def |<[B](f: (B) => Unit)(implicit m: ClassTag[B]): List[B] = {
91 | rows map {d: BeanRow =>
92 | val b = d.makeBean[B](titles, m)
93 | f(b)
94 | b
95 | }
96 | }
97 |
98 | def executeRow[B, R <% Result](m: ClassTag[B], f: (B) => R, exec: Boolean): DecoratedResult[BeanTable] = {
99 | if (exec)
100 | collect(rows map {
101 | (d: BeanRow) => (d.show, implicitly[R => Result].apply(f(d.makeBean(titles, m))))
102 | })
103 | else DecoratedResult(BeanTable(titles, Seq[BeanTableRow]()), Success("ok"))
104 | }
105 | }
106 |
107 | case class BeanRow(propertyValues: List[Any]) {
108 | def show = productIterator.mkString("|", "|", "|")
109 |
110 | def !(value: Any) = BeanRow(propertyValues :+ value)
111 | def !!(value: Any) = BeanRow(propertyValues :+ value)
112 |
113 | private [spring] def makeBean[T](propertyNames: List[String], m: Class[_]) = {
114 | val bean = m.newInstance().asInstanceOf[T]
115 | val wrapper = new BeanWrapperImpl(bean)
116 |
117 | for (i <- 0 until propertyNames.size) {
118 | val propertyName = propertyNames(i)
119 | val propertyValue = propertyValues(i)
120 |
121 | wrapper.setPropertyValue(propertyName, propertyValue)
122 | }
123 |
124 | bean
125 | }
126 |
127 | private [spring] def makeBean[T](propertyNames: List[String], m: ClassTag[T]): T = makeBean(propertyNames, m.runtimeClass)
128 |
129 | }
130 |
131 | }
132 |
133 | case class BeanTable(titles: Seq[String], rows: Seq[BeanTableRow]) {
134 | def isSuccess = rows.forall(_.isSuccess)
135 | }
136 | object BeanTable {
137 | def apply[R <% Result](titles: Seq[String], results: Seq[(String, R)]): BeanTable = BeanTable(titles, results.collect { case (v, r) => BeanTableRow(v, r) })
138 | }
139 | case class BeanTableRow(cells: Seq[String], result: Result) {
140 | def isSuccess = result.isSuccess
141 | }
142 | object BeanTableRow {
143 | def apply[R](values: String, result: R)(implicit convert: R => Result): BeanTableRow = BeanTableRow(values.trimEnclosing("|").splitTrim("\\|"), convert(result))
144 | }
145 |
--------------------------------------------------------------------------------
/src/main/scala/org/specs2/spring/DataAccess.scala:
--------------------------------------------------------------------------------
1 | package org.specs2.spring
2 |
3 | import org.springframework.orm.hibernate3.{HibernateCallback, HibernateTemplate}
4 | import org.hibernate.{HibernateException, SessionFactory, Session}
5 | import org.specs2.execute.{Success, Result}
6 | import scala.reflect.ClassTag
7 |
8 | /**
9 | * @author janmachacek
10 | */
11 | trait SqlDataAccess {
12 |
13 |
14 | }
15 |
16 | /**
17 | * Convenience mixin for using Hibernate in your Spring integration tests; includes the ``insert`` method overloads
18 | * that work well with ``org.specs2.spring.BeanTables``.
19 | */
20 | trait HibernateDataAccess {
21 |
22 | private def inSession[A](sessionFactory: SessionFactory)(f: (Session) => A) = {
23 | def openSession = {
24 | try {
25 | (sessionFactory.getCurrentSession, true)
26 | } catch {
27 | case e: HibernateException =>
28 | (sessionFactory.openSession(), false)
29 | }
30 | }
31 |
32 | val (session, joinedExisting) = openSession
33 |
34 | val ret = f(session)
35 |
36 | if (!joinedExisting) {
37 | session.flush()
38 | session.close()
39 | }
40 |
41 | ret
42 | }
43 |
44 | /**
45 | * Removes all entities of the given type
46 | *
47 | * @param entity implicitly supplied class manifest of the entity type to be deleted
48 | * @param sessionFactory the session factory that will have the entities removed
49 | */
50 | def deleteAll[T](implicit entity: ClassTag[T], sessionFactory: SessionFactory) {
51 | inSession(sessionFactory) { s =>
52 | s.createQuery("delete from " + entity.runtimeClass.getName).executeUpdate()
53 | }
54 | }
55 |
56 | import org.specs2.execute._
57 |
58 | /**
59 | * Returns a function that inserts the object and returns Success; the function can be supplied to the BeanTables
60 | * ``|>`` function.
61 | * Typical usage is
62 | * 63 | * implicit var sessionFactory = make-SessionFactory-instance() 64 | * 65 | * "Some service operation" in { 66 | * "age" | "name" | "teamName" | 67 | * 32 ! "Jan" ! "Wheelers" | 68 | * 30 ! "Ani" ! "Team GB" |> insert[Rider] 69 | * 70 | * // tests that rely on the inserted Rider objects 71 | * success 72 | * } 73 | *74 | * 75 | * @return function that inserts the object and returns Success when the insert succeeds. 76 | */ 77 | def insert[T](implicit sessionFactory: SessionFactory): (T => Result) = { 78 | t => inSession(sessionFactory) { s => s.saveOrUpdate(t); Success("ok") } 79 | } 80 | 81 | /** 82 | * Returns a function that runs the supplied function f on the object; then inserts the object and returns Success; 83 | * the function can be supplied to the BeanTables ``|>`` function.
86 | * implicit var sessionFactory = make-SessionFactory-instance() 87 | * 88 | * "Some service operation" in { 89 | * "age" | "name" | "teamName" | 90 | * 32 ! "Jan" ! "Wheelers" | 91 | * 30 ! "Ani" ! "Team GB" |> insert[Rider] { r: Rider => r.addEntry(...) } 92 | * 93 | * // tests that rely on the inserted Rider objects; each with one Entry inserted in the function given 94 | * // to the insert[Rider] method 95 | * success 96 | * } 97 | *98 | * 99 | * @param f function that operates on the instance ``T``; this function will run before the Hibernate save. 100 | * @return function that inserts the object and returns Success when the insert succeeds. 101 | */ 102 | def insert[T, R](f: T => R)(implicit sessionFactory: SessionFactory): (T => Result) = { 103 | t => 104 | f(t) 105 | inSession(sessionFactory) { _.saveOrUpdate(t) } 106 | Success("ok") 107 | } 108 | 109 | } 110 | 111 | trait HibernateTemplateDataAccess { 112 | 113 | /** 114 | * Returns a function that runs the supplied function f on the object; then inserts the object and returns Success; 115 | * the function can be supplied to the BeanTables ``|>`` function.
118 | * implicit var hibernateTemplate = make-HibernateTemplate-instance() 119 | * 120 | * "Some service operation" in { 121 | * "age" | "name" | "teamName" | 122 | * 32 ! "Jan" ! "Wheelers" | 123 | * 30 ! "Ani" ! "Team GB" |> insert[Rider] { r: Rider => r.addEntry(...) } 124 | * 125 | * // tests that rely on the inserted Rider objects; each with one Entry inserted in the function given 126 | * // to the insert[Rider] method 127 | * success 128 | * } 129 | *130 | * 131 | * @param f function that operates on the instance ``T``; this function will run before the Hibernate save. 132 | * @return function that inserts the object and returns Success when the insert succeeds. 133 | */ 134 | def insert[T, R](f: T => R)(implicit hibernateTemplate: HibernateTemplate): (T => Result) = { 135 | t => 136 | f(t) 137 | hibernateTemplate.saveOrUpdate(t) 138 | Success("ok") 139 | } 140 | 141 | /** 142 | * Returns a function that inserts the object and returns Success; the function can be supplied to the BeanTables 143 | * ``|>`` function. 144 | * Typical usage is 145 | *
146 | * implicit var hibernateTemplate = make-HibernateTemplate-instance() 147 | * 148 | * "Some service operation" in { 149 | * "age" | "name" | "teamName" | 150 | * 32 ! "Jan" ! "Wheelers" | 151 | * 30 ! "Ani" ! "Team GB" |> insert[Rider] 152 | * 153 | * // tests that rely on the inserted Rider objects 154 | * success 155 | * } 156 | *157 | * 158 | * @return function that inserts the object and returns Success when the insert succeeds. 159 | */ 160 | def insert[T](implicit hibernateTemplate: HibernateTemplate): (T => Result) = { 161 | t => hibernateTemplate.saveOrUpdate(t); Success("ok") 162 | } 163 | 164 | /** 165 | * Removes all entities of the given type 166 | * 167 | * @param entity implicitly supplied class manifest of the entity type to be deleted 168 | * @param hibernateTemplate HibernateTemplate instance that will have the entities removed 169 | */ 170 | def deleteAll[T](implicit entity: ClassTag[T], hibernateTemplate: HibernateTemplate) { 171 | hibernateTemplate.execute(new HibernateCallback[Int] { 172 | def doInHibernate(session: Session) = 173 | session.createQuery("delete from " + entity.runtimeClass.getName).executeUpdate() 174 | }) 175 | } 176 | 177 | } -------------------------------------------------------------------------------- /src/main/scala/org/specs2/spring/SettableEnvironment.scala: -------------------------------------------------------------------------------- 1 | package org.specs2.spring 2 | 3 | 4 | /** 5 | * @author janmachacek 6 | */ 7 | trait SettableEnvironment { 8 | this: SpecificationEnvironment => 9 | 10 | def addJndiEntry(name: String, value: AnyRef) { 11 | environmentSetter.add(name, value) 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/scala/org/specs2/spring/SpecificationLike.scala: -------------------------------------------------------------------------------- 1 | package org.specs2.spring 2 | 3 | import org.springframework.jdbc.core.JdbcTemplate 4 | import org.springframework.orm.hibernate3.HibernateTemplate 5 | import org.springframework.transaction.PlatformTransactionManager 6 | import org.specs2.spring.TestTransactionDefinitionExtractor.TestTransactionDefinition 7 | import org.specs2.specification.{Step, SpecStart, Example} 8 | 9 | /** 10 | * Gives access to the Sprnig context for the specification 11 | */ 12 | trait SpecificationContext { 13 | 14 | private[spring] def testContext: TestContext 15 | 16 | } 17 | 18 | /** 19 | * Gives access to the JNDI environment for the specification 20 | */ 21 | trait SpecificationEnvironment { 22 | 23 | private[spring] def environmentSetter: JndiEnvironmentSetter 24 | 25 | } 26 | 27 | /** 28 | * Mutable Specification that sets up the JNDI environment and autowires the fields / setters of its subclasses. 29 | * 30 | * @author janmachacek 31 | */ 32 | trait SpecificationLike extends org.specs2.mutable.SpecificationLike 33 | with SpecificationContext 34 | with SpecificationEnvironment { 35 | 36 | private[spring] val testContext = new TestContext 37 | private[spring] val environmentSetter = new JndiEnvironmentSetter 38 | 39 | private def setup() { 40 | environmentSetter.prepareEnvironment(new EnvironmentExtractor().extract(this)) 41 | testContext.createAndAutowire(this) 42 | } 43 | 44 | override def is: org.specs2.specification.Fragments = { 45 | // setup the specification's transactional behaviour 46 | val ttd = new TestTransactionDefinitionExtractor().extract(this) 47 | val transformedFragments = 48 | if (ttd == TestTransactionDefinition.NOT_TRANSACTIONAL) 49 | // no transactions required 50 | fragments 51 | else { 52 | // transactions required, run each example body in a [separate] transaction 53 | fragments.map { 54 | case e: Example => 55 | Example(e.desc, { 56 | if (!testContext.loaded()) failure("No ApplicationContext prepared for the test. Did you forget the @ContextConfiguration annotation?") 57 | else { 58 | val transactionManager = testContext.getBean(ttd.getTransactionManagerName, classOf[PlatformTransactionManager]) 59 | if (transactionManager == null) failure("Test marked @Transactional, but no PlatformTransactionManager bean found.") 60 | else { 61 | val transactionStatus = transactionManager.getTransaction(ttd.getTransactionDefinition) 62 | try { 63 | val result = e.execute 64 | if (!ttd.isDefaultRollback) transactionManager.commit(transactionStatus) 65 | result 66 | } finally { 67 | if (ttd.isDefaultRollback) transactionManager.rollback(transactionStatus) 68 | } 69 | } 70 | } 71 | }) 72 | case f => f 73 | } 74 | } 75 | 76 | args(sequential = true) ^ Step(setup) ^ transformedFragments 77 | } 78 | 79 | } -------------------------------------------------------------------------------- /src/test/resources/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Bundle-ManifestVersion: 2 3 | Premain-Class: org.springframework.instrument.InstrumentationSavingAgent 4 | 5 | -------------------------------------------------------------------------------- /src/test/resources/META-INF/spring/module-context.xml: -------------------------------------------------------------------------------- 1 | 2 |