Add support for Microsoft's testing framework

This commit is contained in:
markpollack
2009-04-21 05:36:10 +00:00
parent e2aae1714e
commit ed120b2f52
14 changed files with 1503 additions and 2 deletions

View File

@@ -0,0 +1,5 @@
using System;
using System.Reflection;
[assembly: AssemblyTitle("Spring.Testing.Microsoft")]
[assembly: AssemblyDescription("Interfaces and classes that provide Microsoft Unit Testing integration in Spring.Net")]

View File

@@ -0,0 +1,115 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{2E10A8D3-B8FF-4699-90B8-E621659C58DD}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Spring</RootNamespace>
<AssemblyName>Spring.Testing.Microsoft</AssemblyName>
<FileUpgradeFlags>
</FileUpgradeFlags>
<OldToolsVersion>2.0</OldToolsVersion>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<PublishUrl>http://localhost/Spring.Testing.Microsoft/</PublishUrl>
<Install>true</Install>
<InstallFrom>Web</InstallFrom>
<UpdateEnabled>true</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>true</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\..\..\build\VS.NET.2005\Spring.Testing.Microsoft\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG;NET_2_0</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>..\..\..\build\VS.NET.2005\Spring.Testing.Microsoft\Debug\Spring.Testing.Microsoft.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\..\..\build\VS.NET.2005\Spring.Testing.Microsoft\Release\</OutputPath>
<DefineConstants>TRACE;NET_2_0</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Common.Logging, Version=1.1.0.2, Culture=neutral, PublicKeyToken=65e474d141e25e07, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\lib\net\2.0\Common.Logging.dll</HintPath>
</Reference>
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="System" />
<Reference Include="System.Data" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\CommonAssemblyInfo.cs">
<Link>CommonAssemblyInfo.cs</Link>
</Compile>
<Compile Include="AssemblyInfo.cs" />
<Compile Include="Testing\Microsoft\AbstractDependencyInjectionSpringContextTests.cs" />
<Compile Include="Testing\Microsoft\AbstractSpringContextTests.cs" />
<Compile Include="Testing\Microsoft\AbstractTransactionalDbProviderSpringContextTests.cs" />
<Compile Include="Testing\Microsoft\AbstractTransactionalSpringContextTests.cs" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
<Visible>False</Visible>
<ProductName>.NET Framework Client Profile</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.2.0">
<Visible>False</Visible>
<ProductName>.NET Framework 2.0 %28x86%29</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.0">
<Visible>False</Visible>
<ProductName>.NET Framework 3.0 %28x86%29</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>false</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Spring.Core\Spring.Core.2008.csproj">
<Project>{710961A3-0DF4-49E4-A26E-F5B9C044AC84}</Project>
<Name>Spring.Core.2008</Name>
</ProjectReference>
<ProjectReference Include="..\Spring.Data\Spring.Data.2008.csproj">
<Project>{AE00E5AB-C39A-436F-86D2-33BFE33E2E40}</Project>
<Name>Spring.Data.2008</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -0,0 +1,370 @@
#region License
/*
/// Copyright 2002-2006 the original author or authors.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
*/
#endregion
using System;
using System.Collections;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Spring.Context;
using Spring.Context.Support;
using Spring.Objects.Factory;
using Spring.Objects.Factory.Config;
using Spring.Objects.Factory.Support;
namespace Spring.Testing.Microsoft
{
/// <summary>
/// Convenient superclass for tests depending on a Spring context.
/// The test instance itself is populated by Dependency Injection.
/// </summary>
///
/// <remarks>
/// <p>Really for integration testing, not unit testing.
/// You should <i>not</i> normally use the Spring container
/// for unit tests: simply populate your objects in plain NUnit tests!</p>
///
/// <p>This supports two modes of populating the test:
/// <ul>
/// <li>Via Property Dependency Injection. Simply express dependencies on objects
/// in the test fixture, and they will be satisfied by autowiring by type.</li>
/// <li>Via Field Injection. Declare protected variables of the required type
/// which match named beans in the context. This is autowire by name,
/// rather than type. This approach is based on an approach originated by
/// Ara Abrahmian. Property Dependency Injection is the default: set the
/// "populateProtectedVariables" property to true in the constructor to switch
/// on Field Injection.</li>
/// </ul></p>
///
/// <p>This class will normally cache contexts based on a <i>context key</i>:
/// normally the config locations String array describing the Spring resource
/// descriptors making up the context. Unless the <code>SetDirty()</code> method
/// is called by a test, the context will not be reloaded, even across different
/// subclasses of this test. This is particularly beneficial if your context is
/// slow to construct, for example if you are using Hibernate and the time taken
/// to load the mappings is an issue.</p>
///
/// <p>If you don't want this behavior, you can override the <code>ContextKey</code>
/// property, most likely to return the test class. In conjunction with this you would
/// probably override the <code>GetContext</code> method, which by default loads
/// the locations specified by the <code>ConfigLocations</code> property.</p>
///
/// <p><b>WARNING:</b> When doing integration tests from within VS.NET, only use
/// assembly resource URLs. Else, you may see misleading failures when changing
/// context locations.</p>
/// </remarks>
///
/// <author>Rod Johnson</author>
/// <author>Rob Harrop</author>
/// <author>Rick Evans</author>
/// <author>Aleksandar Seovic (.NET)</author>
public abstract class AbstractDependencyInjectionSpringContextTests : AbstractSpringContextTests
{
private bool populateProtectedVariables = false;
private AutoWiringMode autowireMode = AutoWiringMode.ByType;
private bool dependencyCheck = true;
/// <summary>
/// Application context this test will run against.
/// </summary>
protected IConfigurableApplicationContext applicationContext;
/// <summary>
/// Holds names of the fields that should be used for field injection.
/// </summary>
protected string[] managedVariableNames;
private int loadCount = 0;
/// <summary>
/// Default constructor for AbstractDependencyInjectionSpringContextTests.
/// </summary>
public AbstractDependencyInjectionSpringContextTests()
{}
/// <summary>
/// Gets or sets a flag specifying whether to populate protected
/// variables of this test case.
/// </summary>
/// <value>
/// A flag specifying whether to populate protected variables of this test case.
/// Default is <b>false</b>.
/// </value>
public bool PopulateProtectedVariables
{
get { return populateProtectedVariables; }
set { populateProtectedVariables = value; }
}
/// <summary>
/// Gets or sets the autowire mode for test properties set by Dependency Injection.
/// </summary>
/// <value>
/// The autowire mode for test properties set by Dependency Injection.
/// The default is <see cref="AutoWiringMode.ByType"/>.
/// </value>
public AutoWiringMode AutowireMode
{
get { return autowireMode; }
set { autowireMode = value; }
}
/// <summary>
/// Gets or sets a flag specifying whether or not dependency checking
/// should be performed for test properties set by Dependency Injection.
/// </summary>
/// <value>
/// <p>A flag specifying whether or not dependency checking
/// should be performed for test properties set by Dependency Injection.</p>
/// <p>The default is <b>true</b>, meaning that tests cannot be run
/// unless all properties are populated.</p>
/// </value>
public bool DependencyCheck
{
get { return dependencyCheck; }
set { dependencyCheck = value; }
}
/// <summary>
/// Gets the current number of context load attempts.
/// </summary>
public int LoadCount
{
get { return loadCount; }
}
/// <summary>
/// Called to say that the "applicationContext" instance variable is dirty and
/// should be reloaded. We need to do this if a test has modified the context
/// (for example, by replacing an object definition).
/// </summary>
public void SetDirty()
{
SetDirty(ConfigLocations);
}
/// <summary>
/// Test setup method.
/// </summary>
[TestInitialize]
public virtual void TestInitialize()
{
this.applicationContext = GetContext(ContextKey);
InjectDependencies();
try
{
OnTestInitialize();
}
catch (Exception ex)
{
logger.Error("Setup error", ex);
throw;
}
}
/// <summary>
/// Inject dependencies into 'this' instance (that is, this test instance).
/// </summary>
/// <remarks>
/// <p>The default implementation populates protected variables if the
/// <see cref="PopulateProtectedVariables"/> property is set, else
/// uses autowiring if autowiring is switched on (which it is by default).</p>
/// <p>You can certainly override this method if you want to totally control
/// how dependencies are injected into 'this' instance.</p>
/// </remarks>
protected virtual void InjectDependencies()
{
if (PopulateProtectedVariables)
{
if (this.managedVariableNames == null)
{
InitManagedVariableNames();
}
InjectProtectedVariables();
}
else if (AutowireMode != AutoWiringMode.No)
{
IConfigurableListableObjectFactory factory = this.applicationContext.ObjectFactory;
((AbstractObjectFactory) factory).IgnoreDependencyType(typeof(AutoWiringMode));
factory.AutowireObjectProperties(this, AutowireMode, DependencyCheck);
}
}
/// <summary>
/// Gets a key for this context. Usually based on config locations, but
/// a subclass overriding buildContext() might want to return its class.
/// </summary>
protected virtual object ContextKey
{
get { return ConfigLocations; }
}
/// <summary>
/// Loads application context from the specified resource locations.
/// </summary>
/// <param name="locations">Resources to load object definitions from.</param>
protected override IConfigurableApplicationContext LoadContextLocations(string[] locations)
{
++this.loadCount;
return base.LoadContextLocations(locations);
}
/// <summary>
/// Retrieves the names of the fields that should be used for field injection.
/// </summary>
protected virtual void InitManagedVariableNames()
{
ArrayList managedVarNames = new ArrayList();
Type type = GetType();
do
{
FieldInfo[] fields =
type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Instance);
if (logger.IsDebugEnabled)
{
logger.Debug("Found " + fields.Length + " fields on " + type);
}
for (int i = 0; i < fields.Length; i++)
{
FieldInfo field = fields[i];
if (logger.IsDebugEnabled)
{
logger.Debug("Candidate field: " + field);
}
if (IsProtectedInstanceField(field))
{
object oldValue = field.GetValue(this);
if (oldValue == null)
{
managedVarNames.Add(field.Name);
if (logger.IsDebugEnabled)
{
logger.Debug("Added managed variable '" + field.Name + "'");
}
}
else
{
if (logger.IsDebugEnabled)
{
logger.Debug("Rejected managed variable '" + field.Name + "'");
}
}
}
}
type = type.BaseType;
} while (type != typeof (AbstractDependencyInjectionSpringContextTests));
this.managedVariableNames = (string[]) managedVarNames.ToArray(typeof (string));
}
private static bool IsProtectedInstanceField(FieldInfo field)
{
return field.IsFamily;
}
/// <summary>
/// Injects protected fields using Field Injection.
/// </summary>
protected virtual void InjectProtectedVariables()
{
for (int i = 0; i < this.managedVariableNames.Length; i++)
{
string fieldName = this.managedVariableNames[i];
Object obj = null;
try
{
FieldInfo field = GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
if (field != null)
{
BeforeProtectedVariableInjection(field);
obj = this.applicationContext.GetObject(fieldName, field.FieldType);
field.SetValue(this, obj);
if (logger.IsDebugEnabled)
{
logger.Debug("Populated field: " + field);
}
}
else
{
if (logger.IsWarnEnabled)
{
logger.Warn("No field with name '" + fieldName + "'");
}
}
}
catch (NoSuchObjectDefinitionException)
{
if (logger.IsWarnEnabled)
{
logger.Warn("No object definition with name '" + fieldName + "'");
}
}
}
}
/// <summary>
/// Called right before a field is being injected
/// </summary>
protected virtual void BeforeProtectedVariableInjection(FieldInfo fieldInfo)
{
}
/// <summary>
/// Subclasses can override this method in order to
/// add custom test setup logic.
/// </summary>
protected virtual void OnTestInitialize()
{}
/// <summary>
/// Test teardown method.
/// </summary>
[TestCleanup]
public void TestCleanup()
{
try
{
OnTestCleanup();
}
catch (Exception ex)
{
logger.Error("OnTearDown error", ex);
}
}
/// <summary>
/// Subclasses can override this method in order to
/// add custom test teardown logic.
/// </summary>
protected virtual void OnTestCleanup()
{}
/// <summary>
/// Subclasses must implement this property to return the locations of their
/// config files. A plain path will be treated as a file system location.
/// </summary>
/// <value>An array of config locations</value>
protected abstract string[] ConfigLocations { get; }
}
}

View File

@@ -0,0 +1,238 @@
#region License
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Collections;
using Common.Logging;
using Spring.Context;
using Spring.Context.Support;
using Spring.Util;
namespace Spring.Testing.Microsoft
{
/// <summary>
/// Superclass for NUnit test cases using a Spring context.
/// </summary>
/// <remarks>
/// <p>Maintains a cache of contexts by key. This has significant performance
/// benefit if initializing the context would take time. While initializing a
/// Spring context itself is very quick, some objects in a context, such as
/// a LocalSessionFactoryObject for working with NHibernate, may take time to
/// initialize. Hence it often makes sense to do that initializing once.</p>
/// <p>Normally you won't extend this class directly but rather extend one
/// of its subclasses.</p>
/// </remarks>
/// <author>Rod Johnson</author>
/// <author>Aleksandar Seovic (.NET)</author>
public abstract class AbstractSpringContextTests
{
/// <summary>
/// Map of context keys returned by subclasses of this class, to
/// Spring contexts.
/// </summary>
private static readonly IDictionary contextKeyToContextMap;
/// <summary>
/// Static ctor to avoid "beforeFieldInit" problem.
/// </summary>
static AbstractSpringContextTests()
{
contextKeyToContextMap = new Hashtable();
}
/// <summary>
/// Disposes any cached context instance and removes it from cache.
/// </summary>
public static void ClearContextCache()
{
foreach(IApplicationContext ctx in contextKeyToContextMap.Values)
{
ctx.Dispose();
}
contextKeyToContextMap.Clear();
}
/// <summary>
/// Indicates, whether context instances should be automatically registered with the global <see cref="ContextRegistry"/>.
/// </summary>
private bool registerContextWithContextRegistry = true;
/// <summary>
/// Logger available to subclasses.
/// </summary>
protected readonly ILog logger;
/// <summary>
/// Default constructor for AbstractSpringContextTests.
/// </summary>
protected AbstractSpringContextTests()
{
logger = LogManager.GetLogger(GetType());
}
/// <summary>
/// Controls, whether application context instances will
/// be registered/unregistered with the global <see cref="ContextRegistry"/>.
/// Defaults to <c>true</c>.
/// </summary>
public bool RegisterContextWithContextRegistry
{
get { return registerContextWithContextRegistry; }
set { registerContextWithContextRegistry = value; }
}
/// <summary>
/// Set custom locations dirty. This will cause them to be reloaded
/// from the cache before the next test case is executed.
/// </summary>
/// <remarks>
/// Call this method only if you change the state of a singleton
/// object, potentially affecting future tests.
/// </remarks>
/// <param name="locations">Locations </param>
protected void SetDirty(string[] locations)
{
String keyString = ContextKeyString(locations);
IConfigurableApplicationContext ctx =
(IConfigurableApplicationContext) contextKeyToContextMap[keyString];
contextKeyToContextMap.Remove(keyString);
if (ctx != null)
{
ctx.Dispose();
}
}
/// <summary>
/// Returns <c>true</c> if context for the specified
/// <paramref name="contextKey"/> is cached.
/// </summary>
/// <param name="contextKey">Context key to check.</param>
/// <returns>
/// <c>true</c> if context for the specified
/// <paramref name="contextKey"/> is cached,
/// <c>false</c> otherwise.
/// </returns>
protected bool HasCachedContext(object contextKey)
{
string keyString = ContextKeyString(contextKey);
return contextKeyToContextMap.Contains(keyString);
}
/// <summary>
/// Converts context key to string.
/// </summary>
/// <remarks>
/// Subclasses can override this to return a string representation of
/// their contextKey for use in logging.
/// </remarks>
/// <param name="contextKey">Context key to convert.</param>
/// <returns>
/// String representation of the specified <paramref name="contextKey"/>. Null if
/// contextKey is null.
/// </returns>
protected virtual string ContextKeyString(object contextKey)
{
if (contextKey == null)
{
return null;
}
if (contextKey is string[])
{
return StringUtils.ArrayToCommaDelimitedString((string[]) contextKey);
}
else
{
return contextKey.ToString();
}
}
/// <summary>
/// Caches application context.
/// </summary>
/// <param name="key">Key to use.</param>
/// <param name="context">Context to cache.</param>
public void AddContext(object key, IConfigurableApplicationContext context)
{
AssertUtils.ArgumentNotNull(context, "context", "ApplicationContext must not be null");
string keyString = ContextKeyString(key);
contextKeyToContextMap.Add(keyString, context);
if (RegisterContextWithContextRegistry
&& !ContextRegistry.IsContextRegistered(context.Name))
{
ContextRegistry.RegisterContext(context);
}
}
/// <summary>
/// Returns cached context if present, or loads it if not.
/// </summary>
/// <param name="key">Context key.</param>
/// <returns>Spring application context associated with the specified key.</returns>
protected IConfigurableApplicationContext GetContext(object key)
{
string keyString = ContextKeyString(key);
IConfigurableApplicationContext ctx =
(IConfigurableApplicationContext) contextKeyToContextMap[keyString];
if (ctx == null)
{
if (key is string[])
{
ctx = LoadContextLocations((string[]) key);
}
else
{
ctx = LoadContext(key);
}
AddContext(key, ctx);
}
return ctx;
}
/// <summary>
/// Loads application context from the specified resource locations.
/// </summary>
/// <param name="locations">Resources to load object definitions from.</param>
protected virtual IConfigurableApplicationContext LoadContextLocations(string[] locations)
{
if (logger.IsInfoEnabled)
{
logger.Info("Loading config for: " + StringUtils.ArrayToCommaDelimitedString(locations));
}
return new XmlApplicationContext(locations);
}
/// <summary>
/// Loads application context based on user-defined key.
/// </summary>
/// <remarks>
/// Unless overriden by the user, this method will alway throw
/// a <see cref="NotSupportedException"/>.
/// </remarks>
/// <param name="key">User-defined key.</param>
protected virtual IConfigurableApplicationContext LoadContext(object key)
{
throw new NotSupportedException("Subclasses may override this");
}
}
}

View File

@@ -0,0 +1,125 @@
#region License
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Data;
using Spring.Data.Common;
using Spring.Data.Core;
namespace Spring.Testing.Microsoft
{
/// <summary>
/// Subclass of AbstractTransactionalSpringContextTests that adds some convenience
/// functionality for ADO.NET access. Expects a IDbProvider object
/// to be defined in the Spring application context.
/// </summary>
/// <remarks>
/// This class exposes a AdoTemplate and provides an easy way to delete from the
/// database in a new transaction.
/// </remarks>
/// <author>Rod Johnson</author>
/// <author>Juergen Hoeller</author>
/// <author>Mark Pollack (.NET)</author>
public abstract class AbstractTransactionalDbProviderSpringContextTests : AbstractTransactionalSpringContextTests
{
/// <summary>
/// Holds the <see cref="AdoTemplate"/> that this base class manages
/// </summary>
protected AdoTemplate adoTemplate;
/// <summary>
/// Did this test delete any tables? If so, we forbid transaction completion,
/// and only allow rollback.
/// </summary>
private bool zappedTables;
/// <summary>
/// Initializes a new instance of the <see cref="AbstractTransactionalDbProviderSpringContextTests"/> class.
/// </summary>
public AbstractTransactionalDbProviderSpringContextTests()
{
}
/// <summary>
/// Sets the DbProvider, via Dependency Injection.
/// </summary>
/// <value>The IDbProvider.</value>
public IDbProvider DbProvider
{
set { adoTemplate = new AdoTemplate(value); }
}
/// <summary>
/// Gets or sets the AdoTemplate that this base class manages.
/// </summary>
/// <value>The ADO template.</value>
public AdoTemplate AdoTemplate
{
get { return adoTemplate; }
}
/// <summary>
/// Convenient method to delete all rows from these tables.
/// Calling this method will make avoidance of rollback by calling
/// SetComplete() impossible.
/// </summary>
/// <param name="names"></param>
protected void DeleteFromTables(String[] names)
{
for (int i = 0; i < names.Length; i++)
{
int rowCount = this.adoTemplate.ExecuteNonQuery(CommandType.Text, "DELETE FROM " + names[i]);
if (logger.IsInfoEnabled)
{
logger.Info("Deleted " + rowCount + " rows from table " + names[i]);
}
}
this.zappedTables = true;
}
/// <summary>
/// Overridden to prevent the transaction committing if a number of tables have been
/// cleared, as a defensive measure against accidental <i>permanent</i> wiping of a database.
/// </summary>
protected override void SetComplete()
{
if (this.zappedTables)
{
throw new InvalidOperationException("Cannot set complete after deleting tables");
}
base.SetComplete();
}
/// <summary>
/// Counts the rows in given table.
/// </summary>
/// <param name="tableName">Name of the table to count rows in.</param>
/// <returns>The number of rows in the table</returns>
protected int CountRowsInTable(String tableName)
{
return (int) adoTemplate.ExecuteScalar(CommandType.Text, "SELECT COUNT(0) FROM " + tableName);
}
//TODO ExecuteScript...
}
}

View File

@@ -0,0 +1,306 @@
#region License
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using Spring.Transaction;
using Spring.Transaction.Support;
namespace Spring.Testing.Microsoft
{
/// <summary>
/// Convenient superclass for tests that should occur in a transaction, but normally
/// will roll the transaction back on the completion of each test.
/// </summary>
/// <remarks>
/// <p>This is useful in a range of circumstances, allowing the following benefits:</p>
/// <ul>
/// <li>Ability to delete or insert any data in the database, without affecting other tests</li>
/// <li>Providing a transactional context for any code requiring a transaction</li>
/// <li>Ability to write anything to the database without any need to clean up.</li>
/// </ul>
///
/// <p>This class is typically very fast, compared to traditional setup/teardown scripts.</p>
///
/// <p>If data should be left in the database, call the <code>SetComplete()</code>
/// method in each test. The "DefaultRollback" property, which defaults to "true",
/// determines whether transactions will complete by default.</p>
///
/// <p>It is even possible to end the transaction early; for example, to verify lazy
/// loading behavior of an O/R mapping tool. (This is a valuable away to avoid
/// unexpected errors when testing a web UI, for example.) Simply call the
/// <code>endTransaction()</code> method. Execution will then occur without a
/// transactional context.</p>
///
/// <p>The <code>StartNewTransaction()</code> method may be called after a call to
/// <code>EndTransaction()</code> if you wish to create a new transaction, quite
/// independent of the old transaction. The new transaction's default fate will be to
/// roll back, unless <code>setComplete()</code> is called again during the scope of the
/// new transaction. Any number of transactions may be created and ended in this way.
/// The final transaction will automatically be rolled back when the test case is
/// torn down.</p>
///
/// <p>Transactional behavior requires a single object in the context implementing the
/// IPlatformTransactionManager interface. This will be set by the superclass's
/// Dependency Injection mechanism. If using the superclass's Field Injection mechanism,
/// the implementation should be named "transactionManager". This mechanism allows the
/// use of this superclass even when there's more than one transaction manager in the context.</p>
///
/// <p><i>This superclass can also be used without transaction management, if no
/// IPlatformTransactionManager object is found in the context provided. Be careful about
/// using this mode, as it allows the potential to permanently modify data.
/// This mode is available only if dependency checking is turned off in
/// the AbstractDependencyInjectionSpringContextTests superclass. The non-transactional
/// capability is provided to enable use of the same subclass in different environments.</i></p>
///
/// </remarks>
///
/// <author>Rod Johnson</author>
/// <author>Juergen Hoeller</author>
/// <author>Rick Evans</author>
/// <author>Mark Pollack (.NET)</author>
public abstract class AbstractTransactionalSpringContextTests : AbstractDependencyInjectionSpringContextTests
{
/// <summary>
/// The transaction manager to use
/// </summary>
protected IPlatformTransactionManager transactionManager;
/// <summary>
/// Should we roll back by default?
/// </summary>
private bool defaultRollback = true;
/// <summary>
/// Should we commit the current transaction?
/// </summary>
private bool complete = false;
/// <summary>
/// Number of transactions started
/// </summary>
private int transactionsStarted = 0;
/// <summary>
/// Default transaction definition is used.
/// Subclasses can change this to cause different behaviour.
/// </summary>
private ITransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
/// <summary>
/// TransactionStatus for this test. Typical subclasses won't need to use it.
/// </summary>
protected ITransactionStatus transactionStatus;
/// <summary>
/// Initializes a new instance of the <see cref="AbstractTransactionalSpringContextTests"/> class.
/// </summary>
public AbstractTransactionalSpringContextTests()
{
}
/// <summary>
/// Sets the transaction manager to use.
/// </summary>
public IPlatformTransactionManager TransactionManager
{
set { transactionManager = value; }
}
/// <summary>
/// Sets the default rollback flag.
/// </summary>
public bool DefaultRollback
{
set { defaultRollback = value; }
}
/// <summary>
/// Set the <see cref="ITransactionDefinition"/> to be used
/// </summary>
/// <remarks>
/// Defaults to <see cref="DefaultTransactionDefinition"/>
/// </remarks>
protected ITransactionDefinition TransactionDefinition
{
set { transactionDefinition = value; }
}
/// <summary>
/// Prevents the transaction.
/// </summary>
protected virtual void PreventTransaction()
{
this.transactionDefinition = null;
}
/// <summary>
/// Creates a transaction
/// </summary>
protected override void OnTestInitialize()
{
this.complete = !this.defaultRollback;
if (this.transactionManager == null)
{
logger.Info("No transaction manager set: test will NOT run within a transaction");
}
else if (this.transactionDefinition == null)
{
logger.Info("No transaction definition set: test will NOT run within a transaction");
}
else
{
OnSetUpBeforeTransaction();
StartNewTransaction();
try
{
OnSetUpInTransaction();
}
catch (Exception)
{
EndTransaction();
throw;
}
}
}
/// <summary>
/// Callback method called before transaction is setup.
/// </summary>
protected virtual void OnSetUpBeforeTransaction()
{
}
/// <summary>
/// Callback method called after transaction is setup.
/// </summary>
protected virtual void OnSetUpInTransaction()
{
}
/// <summary>
/// rollback the transaction.
/// </summary>
protected override void OnTestCleanup()
{
// Call onTearDownInTransaction and end transaction if the transaction is still active.
if (this.transactionStatus != null && !this.transactionStatus.Completed)
{
try
{
OnTearDownInTransaction();
}
finally
{
EndTransaction();
}
}
// Call onTearDownAfterTransaction if there was at least one transaction,
// even if it has been completed early through an endTransaction() call.
if (this.transactionsStarted > 0)
{
OnTearDownAfterTransaction();
}
}
/// <summary>
/// Callback before rolling back the transaction.
/// </summary>
protected virtual void OnTearDownInTransaction()
{
}
/// <summary>
/// Callback after rolling back the transaction.
/// </summary>
protected virtual void OnTearDownAfterTransaction()
{
}
/// <summary>
/// Set the complete flag..
/// </summary>
protected virtual void SetComplete()
{
if (this.transactionManager == null)
{
throw new InvalidOperationException("No transaction manager set");
}
this.complete = true;
}
/// <summary>
/// Ends the transaction.
/// </summary>
protected virtual void EndTransaction()
{
if (this.transactionStatus != null)
{
try
{
if (!this.complete)
{
this.transactionManager.Rollback(this.transactionStatus);
logger.Info("Rolled back transaction after test execution");
}
else
{
this.transactionManager.Commit(this.transactionStatus);
logger.Info("Committed transaction after test execution");
}
}
finally
{
this.transactionStatus = null;
}
}
}
/// <summary>
/// Starts the new transaction.
/// </summary>
protected void StartNewTransaction()
{
if (this.transactionStatus != null)
{
throw new InvalidOperationException("Cannot start new transaction without ending existing transaction: " +
"Invoke endTransaction() before startNewTransaction()");
}
if (this.transactionManager == null)
{
throw new InvalidOperationException("No transaction manager set");
}
this.transactionStatus = this.transactionManager.GetTransaction(this.transactionDefinition);
++this.transactionsStarted;
this.complete = !this.defaultRollback;
if (logger.IsInfoEnabled)
{
logger.Info("Began transaction (" + this.transactionsStarted + "): transaction manager [" +
this.transactionManager + "]; default rollback = " + this.defaultRollback);
}
}
}
}

View File

@@ -1,5 +1,5 @@
using System;
using System.Reflection;
[assembly: AssemblyTitle("Spring.Net NUnit support")]
[assembly: AssemblyTitle("Spring.Testing.NUnit")]
[assembly: AssemblyDescription("Interfaces and classes that provide NUnit integration in Spring.Net")]

View File

@@ -0,0 +1,10 @@
using System.Reflection;
using System.Runtime.CompilerServices;
//
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
//
[assembly: AssemblyTitle("Spring.Testing.Microsoft Tests")]
[assembly: AssemblyDescription("Unit tests for Spring.Testing.Microsoft assembly")]

View File

@@ -0,0 +1,87 @@
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{7321149A-47C6-4FBA-9D1A-26FD6815381B}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Spring</RootNamespace>
<AssemblyName>Spring.Testing.Microsoft.Tests</AssemblyName>
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TargetFrameworkSubset>
</TargetFrameworkSubset>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Common.Logging, Version=1.2.0.0, Culture=neutral, PublicKeyToken=af08829b84f0328e">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\lib\Net\2.0\Common.Logging.dll</HintPath>
</Reference>
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<Compile Include="AssemblyInfo.cs" />
<Compile Include="Testing\Microsoft\AbstractDependencyInjectionSpringContextTestsTests.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="Spring.Testing.Microsoft.Tests.dll.config" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Testing\Microsoft\TestApplicationContext.xml" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spring\Spring.Core\Spring.Core.2008.csproj">
<Project>{710961A3-0DF4-49E4-A26E-F5B9C044AC84}</Project>
<Name>Spring.Core.2008</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Spring\Spring.Data\Spring.Data.2008.csproj">
<Project>{AE00E5AB-C39A-436F-86D2-33BFE33E2E40}</Project>
<Name>Spring.Data.2008</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Spring\Spring.Testing.Microsoft\Spring.Testing.Microsoft.2008.csproj">
<Project>{2E10A8D3-B8FF-4699-90B8-E621659C58DD}</Project>
<Name>Spring.Testing.Microsoft.2008</Name>
</ProjectReference>
<ProjectReference Include="..\Spring.Core.Tests\Spring.Core.Tests.2008.csproj">
<Project>{44B16BAA-6DF8-447C-9D7F-3AD3D854D904}</Project>
<Name>Spring.Core.Tests.2008</Name>
</ProjectReference>
<ProjectReference Include="..\Spring.Data.Tests\Spring.Data.Tests.2008.csproj">
<Project>{ACD39D47-1811-40FA-9E7E-5DEA5B9CE6C0}</Project>
<Name>Spring.Data.Tests.2008</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
Copyright 2002-2005 the original author or authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<configuration>
<configSections>
<sectionGroup name="common">
<section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging" />
</sectionGroup>
<sectionGroup name='spring'>
<section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core"/>
</sectionGroup>
</configSections>
<common>
<logging>
<factoryAdapter type="Common.Logging.Simple.NoOpLoggerFactoryAdapter, Common.Logging"/>
</logging>
</common>
<spring>
<parsers>
<parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />
</parsers>
</spring>
</configuration>

View File

@@ -0,0 +1,130 @@
#region License
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Spring.Context;
using Spring.Context.Support;
namespace Spring.Testing.Microsoft
{
/// <summary>
/// Summary description for AbstractDependencyInjectionSpringContextTestsTests.
/// </summary>
[TestClass]
public class AbstractDependencyInjectionSpringContextTestsTests
{
private class TestAbstractDependencyInjectionSpringContextTests :AbstractDependencyInjectionSpringContextTests
{
public static readonly string[] CONFIGLOCATIONS = new string[] {"assembly://Spring.Testing.Microsoft.Tests/Spring.Testing.Microsoft/TestApplicationContext.xml"};
public TestAbstractDependencyInjectionSpringContextTests()
{}
public TestAbstractDependencyInjectionSpringContextTests(bool registerWithContextRegistry)
{
base.RegisterContextWithContextRegistry = registerWithContextRegistry;
}
public IConfigurableApplicationContext ApplicationContext
{
get { return base.applicationContext; }
}
protected override string[] ConfigLocations
{
get { return CONFIGLOCATIONS; }
}
public new bool HasCachedContext(object key)
{
return base.HasCachedContext(key);
}
}
private TestAbstractDependencyInjectionSpringContextTests fixtureInstance ;
[TestCleanup]
public void TearDown()
{
AbstractSpringContextTests.ClearContextCache();
}
[TestMethod]
public void RegistersWithContextRegistryByDefault()
{
fixtureInstance = new TestAbstractDependencyInjectionSpringContextTests();
Assert.IsTrue(fixtureInstance.RegisterContextWithContextRegistry);
}
[TestMethod]
public void UnregistersFromContextRegistryWhenDirty()
{
fixtureInstance = new TestAbstractDependencyInjectionSpringContextTests();
Assert.IsTrue(fixtureInstance.RegisterContextWithContextRegistry);
fixtureInstance.TestInitialize();
Assert.IsTrue( ContextRegistry.IsContextRegistered(fixtureInstance.ApplicationContext.Name) );
fixtureInstance.TestCleanup();
Assert.IsTrue(ContextRegistry.IsContextRegistered(fixtureInstance.ApplicationContext.Name));
fixtureInstance.TestInitialize();
Assert.IsTrue(ContextRegistry.IsContextRegistered(fixtureInstance.ApplicationContext.Name));
fixtureInstance.SetDirty();
fixtureInstance.TestCleanup();
Assert.IsFalse(ContextRegistry.IsContextRegistered(fixtureInstance.ApplicationContext.Name));
}
[TestMethod]
public void DoesNotRegisterContextWithContextRegistry()
{
fixtureInstance = new TestAbstractDependencyInjectionSpringContextTests();
Assert.IsTrue(fixtureInstance.RegisterContextWithContextRegistry);
fixtureInstance.RegisterContextWithContextRegistry = false;
fixtureInstance.TestInitialize();
Assert.IsFalse( ContextRegistry.IsContextRegistered(fixtureInstance.ApplicationContext.Name) );
fixtureInstance.TestCleanup();
Assert.IsFalse( ContextRegistry.IsContextRegistered(fixtureInstance.ApplicationContext.Name) );
}
[TestMethod]
public void CachesApplicationContexts()
{
fixtureInstance = new TestAbstractDependencyInjectionSpringContextTests(false);
fixtureInstance.TestInitialize();
Assert.IsNotNull(fixtureInstance.ApplicationContext);
Assert.AreEqual(1, fixtureInstance.LoadCount); // context has been loaded
Assert.IsTrue(fixtureInstance.HasCachedContext(TestAbstractDependencyInjectionSpringContextTests.CONFIGLOCATIONS));
fixtureInstance.TestCleanup();
TestAbstractDependencyInjectionSpringContextTests otherFixtureInstance = new TestAbstractDependencyInjectionSpringContextTests(false);
otherFixtureInstance.TestInitialize();
Assert.IsNotNull(otherFixtureInstance.ApplicationContext);
Assert.AreEqual(0, otherFixtureInstance.LoadCount); // context was obtained from cache
Assert.AreSame(fixtureInstance.ApplicationContext, otherFixtureInstance.ApplicationContext);
otherFixtureInstance.SetDirty(); // purge cache and dispose cached instances
Assert.IsFalse(fixtureInstance.HasCachedContext(TestAbstractDependencyInjectionSpringContextTests.CONFIGLOCATIONS));
otherFixtureInstance.TestCleanup();
otherFixtureInstance = new TestAbstractDependencyInjectionSpringContextTests(false);
otherFixtureInstance.TestInitialize();
Assert.IsNotNull(otherFixtureInstance.ApplicationContext);
Assert.AreEqual(1, otherFixtureInstance.LoadCount); // context was reloaded because of SetDirty() above
Assert.AreNotSame(fixtureInstance.ApplicationContext, otherFixtureInstance.ApplicationContext);
otherFixtureInstance.TestCleanup();
}
}
}

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8" ?>
<objects xmlns="http://www.springframework.net"
xmlns:db="http://www.springframework.net/database">
<db:provider id="DbProvider"
provider="SqlServer-2.0"
connectionString="Data Source=.\SQL2005;Database=Spring;User ID=springqa;Password=springqa;Trusted_Connection=False"/>
<object id="transactionManager" type="Spring.Transaction.CallCountingTransactionManager, Spring.Data.Tests">
</object>
<object name="Joe" type="Spring.Objects.TestObject, Spring.Core.Tests">
<property name="Name" value="Joe"/>
</object>
</objects>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="common">
<section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging" />
</sectionGroup>
<sectionGroup name='spring'>
<section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core"/>
</sectionGroup>
</configSections>
<common>
<logging>
<factoryAdapter type="Common.Logging.Simple.NoOpLoggerFactoryAdapter, Common.Logging"/>
</logging>
</common>
<spring>
<parsers>
<parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />
</parsers>
</spring>
<startup>
<supportedRuntime version="v2.0.50727"/>
</startup>
</configuration>

View File

@@ -1,4 +1,23 @@
using System;
#region License
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using NUnit.Framework;
using Spring.Context;
using Spring.Context.Support;