This .Net Core 3.1 (NUnit 3) test works under Visual Studio 2019.
But as soon I use JetBrains Rider the test fails because the static property (DataA) returns null in mostly all contexts.
Has some a idea what goes wrong and how to solve this issue?
public static class ClassA
{
public static string DataA { get; set; }
public static string DataB { get; set; }
public static void SetAppConfig(string appSettingsPfad)
{
IList<JProperty> appSettings = JsonConvert.DeserializeObject<JToken>(appSettingsPfad).Select(j => (JProperty)j).ToList();
var appKonfiguration = typeof(ClassA).GetProperties(BindingFlags.Public | BindingFlags.Static);
foreach (JProperty property in appSettings)
{
var appKonfigurationProperty = appKonfiguration.SingleOrDefault(p => p.Name.Equals(property.Name, StringComparison.OrdinalIgnoreCase));
var value = ((JValue)property.Value).Value;
if (appKonfigurationProperty != null)
appKonfigurationProperty.SetValue(null, Convert.ChangeType(value, appKonfigurationProperty.PropertyType));
}
}
}
[TestFixture]
public class DummyTest
{
[Test]
public void TestA()
{
LoadAppConfig();
// DataA returns null here
Console.WriteLine(ClassA.DataA);
new BusinessLogicClass.DoSomething();
// [...]
}
private void LoadAppConfig() {
// [...]
string appSettings = File.ReadAllText(jsonConfigFiles.Single());
ClassA.SetAppConfig(appSettings);
// here it works and a result is the proper string
Console.WriteLine(ClassA.DataA);
// [...]
}
}
public class BusinessLogicClass {
public void DoSomething() {
// DataA returns null here
Console.WriteLine(ClassA.DataA);
// [...]
}
}
(I have simplfied this code to the bare minimum, the orginal code would run into a Exception after a null check)
Related
I'm trying to customize some of nUnits behaviour, however I'm constantly hitting a brick wall
because of nUnits heavy use of code reflection. Test methods (and also setup methods etc) are passed all the way down, deep into the framework, and are converted into a delegate at the latest step possible.
The classes I'm interested in are called TestCommands and only there the framework becomes functional.
For reference here is a snippet I found in nUnits source of the TestMethodCommand class which propably is the bread and butter test execution delegate.
public class TestMethodCommand : TestCommand
{
private readonly TestMethod testMethod;
private readonly object[] arguments;
public TestMethodCommand(TestMethod testMethod) : base(testMethod)
{
this.testMethod = testMethod;
this.arguments = testMethod.Arguments;
}
public override TestResult Execute(TestExecutionContext context)
{
object result = InvokeTestMethod(context); // missing a branch deciding about sync vs. async
// missing some code that checks object against "expected result"
return context.CurrentResult;
}
private object InvokeTestMethod(TestExecutionContext context)
{
return testMethod.Method.Invoke(context.TestObject, arguments);
}
}
I'm puzzled why nUnit couldn't wrap the test method into an Func<object> way way sooner and just pass the context along. As it stands for now if I don't have a MethodInfo nUnit can't run it.
In case you wonder, here is an example of a thing I want to do but I ran into the same problem in other instances as well.
[Scenario(When: "Device Registration reads out PCB Type",
Then: "Device Type might change")]
public void Identifier_Changes_Are_Recognized()
{
var changedType = reference.ChangeType(DeviceType.Terminal);
var changedID = reference.ChangeID(123456);
Assert.Multiple(() =>
{
AssertIsSameDevice(reference, changedType);
AssertIsDifferentDevice(reference, changedID);
});
}
This scenario attribute is supposed to print a small description like so.
public void RunBeforeTest()
{
var text = new MultiLineText
("Scenario:",
"\tGiven:\t" + When,
"\tThen:\t" + Then,
"-------------\n"
);
Console.WriteLine(text);
}
I reaaallly want to tell nUnit "Look, here is an action, please run it" but for the time beeing this seems very hard to achieve. Did anyone else here run in these kinds of problems?
Are there possibly ways to achieve what I'm trying to do? Maybe create my own TestCommand, but as I mentioned, these objects only get created very deep into the framework.
OP here (Writing from my home account)
I looked more into this and actually found a working solution:
public class ArbitraryCodeExecutionWrapper : DelegatingTestCommand
{
public ArbitraryCodeExecutionWrapper(TestCommand innerCommand) : base(innerCommand)
{
}
public Action<TestExecutionContext> BeforeTest { get; init; } = _ => { };
public Action<Test, TestResult> AfterTest { get; init; } = (_,_) => { };
public override TestResult Execute(TestExecutionContext context)
{
BeforeTest(context);
var result = innerCommand.Execute(context);
AfterTest(context.CurrentTest, result);
return result;
}
}
public class NUnitTestCommandWrapperAttribute : Attribute, IWrapTestMethod
{
protected virtual void BeforeTest(TestExecutionContext context)
{
}
protected virtual void AfterTest(Test test, TestResult result)
{
}
public TestCommand Wrap(TestCommand command)
=> new ArbitraryCodeExecutionWrapper(command)
{
BeforeTest = BeforeTest,
AfterTest = AfterTest
};
}
public class ScenarioAttribute : NUnitTestCommandWrapperAttribute
{
public string When { get; init; } = "";
public string Then { get; init; } = "";
protected override void BeforeTest(TestExecutionContext context)
{
var text = new MultiLineText
("Scenario:",
"\tGiven:\t" + When,
"\tThen:\t" + Then
);
Console.WriteLine(text);
}
protected override void AfterTest(Test test, TestResult result)
{
Console.WriteLine("After Test");
}
}
[TestFixture]
public class TestCodeExecution
{
[Test]
[Scenario(When = "nUnit Comes here",
Then = "Print Hello World")]
public void Try_Out_Code_Execution()
{
Console.WriteLine("Hello World");
}
}
public class MultiLineText
{
private List<string> items = new();
public static implicit operator string(MultiLineText text) => text.ToString();
public MultiLineText(params string[] lines)
{
items = lines.ToList();
}
public override string ToString() => string.Join("\n", items);
}
I have a public method ValidateWords inside FooService.To test the ValidateWord method, I created IAppSettingWrapper and AppSettingWrapper which returns the Instance of AppSettings.
Inside the test method, I want to substitute NotAllowedWords using NSubstitute. However, it throws an object reference exception. Is there any way for substitution? If it's not possible, how can I refactor my static instance?
public sealed class AppSettings
{
private static object _lockObject = new object();
private static volatile AppSettings? _instance;
private static DateTime _cacheTime;
private Settings[] _settings;
public AppSettings()
{
try
{
_settings = GetSettings();
}
catch { }
}
public static AppSettings Instance
{
get
{
lock (_lockObject)
{
if (_instance == null)
{
_instance = new AppSettings();
}
}
return _instance;
}
}
public List<string> NotAllowedWords
{
get
{
return new List<string>() {
"index",
"change"
};
}
}
public T GetValues<T>(string key,T defaultValue)
{
T result = defaultValue;
var settings = _settings.Where(i => i.Key == key).FirstOrDefault();
result = (T)Convert.ChangeType(settings.Value, typeof(T));
return result;
}
private Settings[]? GetSettings()
{
//gets data from web services
return base.Channel.GetSettings();
}
}
public class Settings
{
public string Key { get; set; }
public string Value { get; set; }
}
public interface IAppSettingsWrapper
{
public AppSettings Instance();
}
public class AppSettingsWrapper : IAppSettingsWrapper
{
public AppSettings Instance()
{
return AppSettings.Instance;
}
}
[TestClass]
public class FooServiceTest{
private IAppSettingsWrapper _appSettingsWrapper;
[TestInitialize]
public void TestInitialize(IAppSettingsWrapper appSettingsWrapper)
{
_appSettingsWrapper = Substitute.For<IAppSettingsWrapper>();
}
private FooService CreateFooService()
{
return new FooService(_appSettingsWrapper);
}
[TestMethod]
public void Throw_Exception_When_Given_Word_Not_Allowed() {
var service = this.CreateFooService();
_appSettingsWrapper.Instance().NotAllowedWords.Returns(new List<string> { "index" });
var word = "index";
Exception ex = Assert.ThrowsException<Exception>(() => service.ValidateWords(word));
Assert.AreEqual("this word is not allowed", ex.Message);
}
}
public class FooService
{
private IAppSettingsWrapper _appSettingsWrapper;
public FooService(IAppSettingsWrapper appSettingsWrapper)
{
_appSettingsWrapper = appSettingsWrapper;
}
public void ValidateWords(string word)
{
if (_appSettingsWrapper.Instance().NotAllowedWords.Contains(word))
{
throw new Exception("this word is not allowed");
}
}
}
The AppSettings.NotAllowedWords property is not substitutable due to it not being virtual and the class being sealed. If you add NSubstitute.Analyzers to your test project it will help you find these cases. (The How NSubstitute Works documentation outlines why this is the case.)
One option is to make AppSettings implement an IAppSettings interface and inject that into FooService (rather than the wrapper). Then you can use a substitute for tests, and AppSettings.Instance for your real code.
I'm trying to test source generator.
Generator:
[Generator]
public class CustomGenerator : ISourceGenerator
{
public void Initialize(GeneratorInitializationContext context) { }
public void Execute(GeneratorExecutionContext context)
{
//...
}
}
Test code:
Compilation inputCompilation = CreateCompilation(#"
namespace MyCode
{
public class Program
{
public static void Main(string[] args)
{
}
}
}
");
var generator = new CustomGenerator();
var driver = CSharpGeneratorDriver.Create(generator);
driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out var outputCompilation, out var diagnostics);
static Compilation CreateCompilation(string source)
=> CSharpCompilation.Create("compilation",
new[] { CSharpSyntaxTree.ParseText(source) },
new[] { MetadataReference.CreateFromFile(typeof(Binder).GetTypeInfo().Assembly.Location) },
new CSharpCompilationOptions(OutputKind.ConsoleApplication));
The problem is that context.SyntaxReceiver always is null, how to fix that?
Let's inspect the definition of the GeneratorExecutionContext.SyntaxReceiver Property:
namespace Microsoft.CodeAnalysis
{
public readonly struct GeneratorExecutionContext
{
// Summary:
// If the generator registered an Microsoft.CodeAnalysis.ISyntaxReceiver during
// initialization, this will be the instance created for this generation pass.
public ISyntaxReceiver? SyntaxReceiver { get; }
}
}
We see that the GeneratorExecutionContext.SyntaxReceiver Property is declared as nullable, meaning that there are cases where it is indeed expected to be null.
The documentation comment reveals that we first have to register our ISyntaxReceiver in ISourceGenerator.Initialize.
As canton7 has pointed out, we first need to register it via the GeneratorInitializationContext.RegisterForSyntaxNotifications Method:
[Generator]
public class CustomGenerator : ISourceGenerator
{
public void Initialize(GeneratorInitializationContext context)
{
context.RegisterForSyntaxNotifications(static () => new CustomReceiver());
}
public void Execute(GeneratorExecutionContext context)
{
Debug.Assert(context.SyntaxReceiver is not null, $"No {nameof(ISyntaxReceiver)} registerd via {nameof(GeneratorInitializationContext.RegisterForSyntaxNotifications)}.");
if (context.SyntaxReceiver is not CustomReceiver receiver)
{
return;
}
Debug.Assert(receiver is not null);
//...
}
}
internal sealed class CustomReceiver : ISyntaxReceiver
{
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
}
}
Keep in mind to avoid expensive operations within the ISyntaxReceiver because it does not support cancellation unlike the Generator itself.
I can't get Moq to mock an object that gets created in a static method.
Here is my moq and code
code:
public interface IConfigHelper
{
string GetConfiguration(string sectionName, string elementName);
}
public class ConfigHelper : IConfigHelper
{
public ConfigHelper() { }
public virtual string GetConfiguration(string sectionName, string elementName)
{
string retValue = String.Empty;
//Does things to get configuration and return a value
return retValue;
}
}
public class myRealClass
{
public myRealClass(){}
public string myworkingMethod()
{
var retValue = String.Empty;
retValue = utilSvc.GetConfigurationValue();
return retValue;
}
}
public static class utilSvc
{
public static string GetConfigurationValue()
{
ConfigHelper configUtil = new ConfigHelper(); //NOT BEING MOCKED
return configUtil.GetConfiguration("sectionName/sectionElement", "ClinicalSystem");
}
}
the Test using Moq
[TestFixture(TestName = "Tests")]
public class Tests
{
private Mock<IConfigHelper> configHelperMOCK;
[SetUp]
public void Setup()
{
configHelperMOCK = new Mock<IConfigHelper>();
}
[Test]
public void serviceIsBPManagementForValidSource()
{
//Arrange
string sectionName = "sectionName/sectionElement";
string clinicalElementName = "ClinicalSystem";
string clinicalElementValue = "Zedmed";
configHelperMOCK.Setup(s => s.GetConfiguration(sectionName, clinicalElementName)).Returns(clinicalElementValue);
//act
// the call to myRealClass
//assert
// test assertions
}
}
The issue that I am having is with this line:
ConfigHelper configUtil = new ConfigHelper(); //NOT BEING MOCKED
I cannot get the moq to Mock the object.
I do not want the code to read the config file. I wish to moq away this instance of ConfigHelper
You can't wrap the static class/method but you can redirect it
public static class UtilSvc
{
static UtilSvc()
{
CreatorFunc = () => new ConfigHelper();
}
public static Func<IConfigHelper> CreatorFunc { get; set; }
public static string GetConfigurationValue()
{
var configUtil = CreatorFunc();
return configUtil.GetConfiguration("sectionName/sectionElement",
"ClinicalSystem");
}
}
and then in the test
//...
private Mock<IConfigHelper> configHelperMOCK;
[SetUp]
public void Setup()
{
configHelperMOCK = new Mock<IConfigHelper>();
UtilService.CreatorFunc = () => configHelperMOCK.Object;
}
//...
You cannot mock static class. I would rather propose to inject that IConfigHelper into the myRealClass. That is the usual way how to decouple dependencies and use DI.
public class myRealClass
{
private IConfigHelper _configHelper;
public myRealClass(IConfigHelper configHelper)
{
_configHelper = configHelper;
}
public string myworkingMethod()
{
var retValue = String.Empty;
retValue = _configHelper.GetConfigurationValue();
return retValue;
}
}
Avoid coupling your code to static classes, which in most cases cause you code be to difficult to maintain and test.
Follow the Explicit Dependencies Principle
Methods and classes should explicitly require (typically through
method parameters or constructor parameters) any collaborating objects
they need in order to function correctly.
Give the article a read. It is short and very informative.
If you want to keep the static class then you wrap the static class behind an abstraction.
public interface IUtilSvc {
string GetConfigurationValue();
}
public class utilSvcWrapper : IUtilSvc {
public string GetConfigurationValue() {
return utilSvc.GetConfigurationValue(); //Calling static service
}
}
Or another option is that utlSvc does not have to be static if can be injected into dependent classes
public class utilSvc : IUtilScv {
private readonly IConfigHelper configUtil;
public utilSvc(IConfigHelper configHelper) {
configUtil = configHelper;
}
public string GetConfigurationValue() {
return configUtil.GetConfiguration("sectionName/sectionElement", "ClinicalSystem");
}
}
Inject the IUtilScv into the dependent class so that it is no longer dependent on static class.
public class myRealClass {
private readonly IUtilScv utilSvc;
//Explicit dependency inject via constructor
public myRealClass(IUtilScv utilSvc) {
this.utilSvc = utilSvc;
}
public string myworkingMethod() {
var retValue = utilSvc.GetConfiguration();
return retValue;
}
}
In that case you don't even need IConfigHelper when testing as it has also been abstracted away. And you only need to mock the dependencies needed for the test.
[TestFixture(TestName = "Tests")]
public class Tests {
private Mock<IUtilScv> utilScvMOCK;
[SetUp]
public void Setup() {
utilScvMOCK = new Mock<IUtilScv>();
}
[Test]
public void serviceIsBPManagementForValidSource() {
//Arrange
var expectedClinicalElementValue = "Zedmed";
utilScvMOCK
.Setup(s => s.GetConfiguration())
.Returns(expectedClinicalElementValue)
.Verifiable();
var sut = new myRealClass(utilScvMOCK.Object);
//Act
var actualClinicalElementValue = sut.myworkingMethod();
//Assert
configHelperMOCK.Verify();
Assert.AreEqual(expectedClinicalElementValue, actualClinicalElementValue);
}
}
This is a basically a class library project which is somehow exposed as a WCF service. The code below is a part of the Data Access Layer. 'db' is an object of a DataContext class. To save a file, we do the following-
public static Guid SaveFile(FileDetails fileDetails)
{
System.Nullable<Guid> id = null;
SystemDataContext.UsingWrite(db =>
{
db.SaveFileData(fileDetails.RunId, fileDetails.FileData, fileDetails.FileExtension, ref id);
});
return id ?? Guid.Empty;
}
Then, the below would execute-
public static void UsingWrite(Action<SoftCashCreditDBDataContext> action)
{
using (var context = new SystemDataContext())
{
try
{
action(context.Write);
}
catch (Exception ex)
{
DataAccessExceptionHandler.HandleExcetion(ex, Config.DataLayerPolicy);
}
}
}
public SystemDataContext()
{
if (_stack == null)
{
_stack = new Stack<SystemDataContext>();
this.Depth = 1;
this.Read = new SoftCashCreditDBDataContext(Config.ReadDatabaseConnection);
this.Write = new SoftCashCreditDBDataContext(Config.WriteDatabaseConnection);
}
else
{
var parent = _stack.Peek();
/// Increment level of node.
this.Depth = parent.Depth + 1;
/// Copy data context from the parent
this.Read = parent.Read;
this.Write = parent.Write;
}
_stack.Push(this);
}
public int Depth { get; private set; }
public bool IsRoot { get { return this.Depth == 1; } }
[ThreadStatic]
private static Stack<SystemDataContext> _stack = null;
public SoftCashCreditDBDataContext Read { get; private set; }
public SoftCashCreditDBDataContext Write { get; private set; }
#region IDisposable Members
public void Dispose()
{
var context = _stack.Pop();
if (context.IsRoot == true)
{
context.Read.Dispose();
context.Write.Dispose();
_stack = null;
}
}
#endregion
}
They have implemented LINQ to SQL here, and created a DBContext class. The 'SaveFileData()' method is actually part of that class, where it just calls an SP inside to save the file.
What I did not follow-
What exactly does the call to UsingWrite() do here? What is passed to the 'Action action' parameter, and what is it doing?
I understand your confusion. They use 2 delegates.
This is passed to the action parameter:
db =>
{
db.SaveFileData(fileDetails.RunId, fileDetails.FileData, fileDetails.FileExtension, ref id);
}
So when UsingWrite is called, the SoftCashCreditDBDataContext delegate which was set in the Write delegate will call SaveFileData.
A simplified example to help you understand Action:
public void Main()
{
Test(x => Debug.Write(x));
}
private void Test(Action<string> testAction)
{
testAction("Bla");
}
This function will call Debug.Write with the argument x, which is a string that is passed to the test action function.