我在一个名为Tests.dll的程序集中隔离了我的C#app的NUnit测试。关联的配置文件名为Tests.dll.config。这是Nunit使用的,而不是我的应用程序的实际配置文件。它看起来像这样(只显示了几个配置选项还有更多):
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="useHostsFile" value="true" />
<add key="importFile" value="true" />
</appSettings>
</configuration>
为了确保我的应用程序经过全面测试,我需要在测试之间更改配置选项。 在我运行了几个测试之后,我想在文件中添加一些新的配置值,并将这些值用于后续测试。我需要添加什么代码呢?
答案 0 :(得分:3)
我建议使用属性useHostsFile和importFile实现接口IConfig。然后,除了在实现IConfig的Class ConfigDefault中,我将删除所有直接依赖此文件。在此实现中,您加载正常的配置文件。对于每个测试,您可以实现另一个继承自IConfig的类。我建议使用Dependecy Injection。 Ninject免费且易于使用。
答案 1 :(得分:2)
我使用此代码:
[TestMethod]
public void Test_general()
{
var cs = new ConnectionStringSettings();
cs.Name = "ConnectionStrings.Oracle";
cs.ConnectionString = "DATA SOURCE=xxx;PASSWORD=xxx;PERSIST SECURITY INFO=True;USER ID=xxx";
cs.ProviderName = "Oracle.DataAccess.Client";
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
//config.ConnectionStrings.ConnectionStrings.Clear();
config.ConnectionStrings.ConnectionStrings.Remove(cs.Name);
config.ConnectionStrings.ConnectionStrings.Add(cs);
config.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection("connectionStrings");
// your code for your test here
}
答案 2 :(得分:1)
这是我对这一挑战的两分钱。简单地说,创建一个新的AppSettings类作为抽象层。在正常操作下,它只是从应用程序配置文件中读取设置。但单元测试可以基于每个线程覆盖设置,允许单元测试与不同设置并行执行。
internal sealed class AppSettings
{
private static readonly AppSettings instance;
private static ConcurrentDictionary<int, AppSettings> threadInstances;
private string _setting1;
private string _setting2;
static AppSettings() { instance = new AppSettings(); }
internal AppSettings(string setting1 = null, string setting2 = null) {
_setting1 = setting1 != null ? setting1 : Properties.Settings.Default.Setting1;
_setting2 = setting2 != null ? setting2 : Properties.Settings.Default.Setting2;
}
internal static AppSettings Instance {
get {
if (threadInstances != null) {
AppSettings threadInstance;
if (threadedInstances.TryGetValue(Thread.CurrentThread.ManagedThreadId, out threadInstance)) {
return threadInstance;
}
}
return instance;
}
set {
if (threadInstances == null) {
lock (instance) {
if (threadInstances == null) {
int numProcs = Environment.ProcessorCount;
int concurrencyLevel = numProcs * 2;
threadInstances = new ConcurrentDictionary<int, AppSettings>(concurrencyLevel, 5);
}
}
}
if (value != null) {
threadInstances.AddOrUpdate(Thread.CurrentThread.ManagedThreadId, value, (key, oldValue) => value);
} else {
AppSettings threadInstance;
threadInstances.TryRemove(Thread.CurrentThread.ManagedThreadId, out threadInstance);
}
}
}
internal static string Setting1 => Instance._setting1;
internal static string Setting2 => Instance._setting2;
}
在应用程序代码中,使用静态属性访问设置:
function void MyApplicationMethod() {
string setting1 = AppSettings.Setting1;
string setting2 = AppSettings.Setting2;
}
在单元测试中,可选择覆盖所选设置:
[TestClass]
public class MyUnitTest
{
[TestCleanup]
public void CleanupTest()
{
//
// Clear any app settings that were applied for the current test runner thread.
//
AppSettings.Instance = null;
}
[TestMethod]
public void MyUnitMethod()
{
AppSettings.Instance = new AppSettings(setting1: "New settings value for current thread");
// Your test code goes here
}
}
注意:由于AppSettings类的所有方法都声明为内部,因此必须使用以下属性使它们对单元测试程序集可见: [assembly:InternalsVisibleTo(“&lt; assembly name&gt;,PublicKey =&lt; public key&gt;”)]
答案 3 :(得分:0)
我有一种情况,我的配置读取器是使用惰性单例模式实现的,只能读取一次。这样更改app.config是不够的,因为已经从原始配置文件中读取了该值。
但是,单例并没有跨越应用程序域边界,您可以为创建的新应用程序域指定app.config。因此,我能够使用以下命令测试Lazy Singleton应用程序设置:
var otherAppDomainSetup = new AppDomainSetup
{
ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
DisallowBindingRedirects = false,
DisallowCodeDownload = true,
ConfigurationFile = Path.Combine(AppContext.BaseDirectory, "Artifacts\\Configs\\OtherApp.config")
};
var otherAppDomain = AppDomain.CreateDomain(friendlyName: "Other", securityInfo: null, info: otherAppDomainSetup);
otherAppDomain.DoCallBack(new CrossAppDomainDelegate(() => Assert.AreEqual(expected: ***, actual: ***static singleton call***)));
AppDomain.Unload(otherAppDomain);
对于非静态调用,请参见在https://docs.microsoft.com/en-us/dotnet/api/system.appdomain?view=netframework-4.7.2
上使用CreateInstanceAndUnwrap的示例