TestCase() in C#- problem with DateTime argument - c#

Hey I wrote a test in C# which as argument takes only one DateTime argument. What I would like to do is to add [TestCase(new DateTime(2022, 1, 31))] above my test but when I try to do that I get an error that "An attribute must be a constant expression, typeof expression or array creation expression of an attribute parameter type". What can I do?

Any use of new in the argument list to TestCaseAttribute will get you the same error. Because of the restrictions that C# imposes, NUnit provides TestCaseSourceAttribute.
However, TestCaseSourceAttribute involves more typing and in this case, you can provide a string as the argument. Note that the argument to the test method should still be typed as a DateTime even though you are providing a string. As noted in one of the comments, NUnit is smart enough to convert it.
[TestCase("2022/1/31")]
public void MyTestMethod(DateTime dt)
{
...
}

If you need multiple test this is the solution
Nunit documentation. If you don't have multiple parameters to test has few sense to pass value from outside you can define a variable inside the test
private static DateTime[] source = new DateTime[]
{
new DateTime(2021,1,12),
new DateTime(2022,2,12),
new DateTime(1988,4,4)
};
[Test, TestCaseSource(nameof(source))]
public void XXX(DateTime dt)
{
}

*BTW. There is a new type DateOnly available now in .NET. *
The most trivial case can be solved by using simpler inputs:
[DataTestMethod]
[DataRow(2022, 1, 31)]
public void Test(int year, int month, int day)
{
Assert.AreEqual(31, new DateOnly(year, month, day));
}
However, DynamicData offers the most flexibility:
public static IEnumerable<object[]> GetTestCases()
{
var testCases = new List<TestCase>()
{
new TestCase()
{
DT = new DateOnly(2022, 1, 31),
ExpectedDay = 31
},
};
return testCases
#if DEBUG //protect from a bad checkin by running all test cases in RELEASE builds
//.Where(tc => tc.Name.Contains("..."))
#endif
.Select(tc => new object[] { tc });
}
[TestMethod]
[DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)]
public void Test(TestCase tc)
{
Assert.AreEqual(tc.ExpectedDay, tc.DT.Day);
}
public class TestCase
{
public DateOnly DT { get; init; }
public int ExpectedDay { get; init; }
}

Suggest using either MemberData property as described in this blog post or replacing it with TheoryData as described here.
Arguments passed to tests must be constants, but you can use either of the above fields to work around that requirement.

Related

C# Confounding Implicitly typed variable with dynamic

If you run TestClass.Test() you will get a RuntimeBinderException. It all starts with var str = DoSomething(obj); implicitly typing to dynamic rather than string. Can someone explain what is happening here? Why does RequiresString(str); work? I understand that you can't call extension methods on dynamic objects, but this whole thing feels a bit dirty and broken to me. It all compiles fine despite obvious type mismatches then fails at runtime.
public static class ExtensionTest
{
public static string ToJsonTest(this object x)
{
return string.Empty;
}
}
public static class TestClass
{
public static void Test()
{
dynamic obj = new ExpandoObject();
obj.var1 = "hello";
var str = DoSomething(obj);
var testObj = RequiresString(str);
var json = testObj.ToJsonTest();
}
public static string DoSomething(object x)
{
return string.Empty;
}
public static TestObj RequiresString(string x)
{
return new TestObj();
}
public class TestObj
{
public int Prop1 { get; set; }
}
}
The call to RequiresString contains dynamic arguments, so it's resolved at runtime using the actual argument's type. The actual type returned by DoSomething is string, so the runtime binder looks for RequiresString(string), which can be successfully found as TestClass.RequiresString. So far, so good.
The next call testObj.ToJsonTest() fails, as you already mentioned, because extension methods on dynamic targets are not resolved, so the next call fails.
Yes, it might feel a little bit dirty because there are (almost) no compile-time checks when dynamics are involved, very much like weakly typed scripting languages. That's why I'd advise using dynamic only if really needed and in the narrowest possible context; in particular, I'd say dynamic should not be seen on class' public surface.

List of Anonymous objects?

I have a class using the List class. When attempting to set up the Add function to add my anonymous type, all I get is errors.
I've been searching for hours and as best I can tell, every example I've seen is doing the same thing. So I don't understand what's wrong.
class fileHistory<Object> : List<Object>
{
public new void Add(DateTime ts, int st)
{
base.Add( new { timeStamp = ts; status = st;} );
}
}
You don't need a generic declaration in your class definition, and you also need to change semicolons to commas:
public class fileHistory : List<Object>
{
public new void Add(DateTime ts, int st)
{
base.Add( new { timeStamp = ts, status = st} );
}
}
Right, you can overwrite (not polymorphism! check I used the word overwrite instead of override) List<T>.Add(T) method, but I believe that you could solve your issue using composition instead of inheritance and your code will work flawlessly:
class fileHistory
{
private readonly List<object> _log = new List<object>();
public void Add(DateTime ts, int st)
{
_log.Add( new { timeStamp = ts; status = st;} );
}
}
BTW, I see three design flaws here:
Anonymous types aren't meant to your use case. If you are adding objects with these two properties and you do it in a concrete use case like yours, maybe you're using anonymous types because of your laziness of designing a class with 2 properties??? ;)
Because of #1, why you would create a list of objects using a generic list? It defeats the entire purpose of generics!!
I find a bad design decision hidding Add of List<T>. Use composition instead of inheritance in these cases. Also, I don't know why you're using identifier re-using with new keyword when C# supports method overloading. In List<T> there's no overload of Add with your input parameters...
My advise is: code isn't fancier because of using fancy syntactic sugar provided by C#. Sometimes you don't need it, and honestly, I believe this is the case.
For those who're worried about LINQ...
Any class might or might not implement IEnumerable<T>. The whole fileHistory class can be iterated with foreach or LINQ and its extension methods implementing IEnumerable<T>:
// Check that I dropped the lazy approach of using
// anonymous types!!
class fileHistory : IEnumerable<FileLog>
{
private readonly List<FileLog> _log = new List<FileLog>();
public IEnumerator<FileLog> GetEnumerator()
{
return _log.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _log.GetEnumerator();
}
public void Add(DateTime ts, int st)
{
_log.Add(new FileLog { timeStamp = ts; status = st;} );
}
}
...and now some class like this can be iterated even when using composition instead of inheritance:
new fileHistory().Where(log => log.DateTime < DateTime.Now);
class fileHistory<Object> : List<Object>
{
public new void Add(DateTime ts, int st)
{
// That's how it's supposed to be
base.Add(new { timeStamp = ts, status = st });
}
}
Hope I've helped!

Moq Verify without It - what kind of compare?

When using Moq with Verify, to assert that a certain method has been called with specified parameters, different kind of syntax is possible; one is the "It" syntax, like this
mock.Verify(c => c.SomeMethod(It.Is<string>(s => s == ExpectedString)));
What happens here is that the parameter that SomeMethod is called with, is checked for equality with ExpectedString. Another possible syntax is without "It":
mock.Verify(c => c.SomeMethod(ExpectedString));
which should give the same result. From what I have been able to find on different forums, the difference is that the latter is an identify check (reference equals) (except for value types).
However, my question is when the parameter is of a type Collection type. In .NET, Equals on Collection<T> is just inherited from object, so the following verify:
mock.Verify(c => c.SomeMethod(new Collection<string> { ExpectedString }));
should not be possible to pass, given that the collection is instantiated in the verify, and thus cannot possibly be the same instance that is instantiated in the production code. Nevertheless, it works, which indicates that Moq does a CollectionAssert or something like that, contrary to what information I could find.
Here is a code example that illustrates the behaviour, the test passes, but I think it should fail if Moq had used reference equals comparison.
[TestMethod]
public void Test()
{
var mock = new Mock<IPrint>();
const int ExpectedParam = 1;
var test = new TestPrinter { Printer = mock.Object, Expected = ExpectedParam };
test.Do();
mock.Verify(c => c.Print(new Collection<int> { ExpectedParam }));
}
public interface IPrint
{
void Print(Collection<int> numbers);
}
public class TestPrinter
{
public IPrint Printer { get; set; }
public int Expected { get; set; }
public void Do()
{
Printer.Print(new Collection<int> { Expected });
}
}
Does anyone know if this is expected behaviour of Moq (version 4.1)? Was the behaviour changed at some version level?
This is desired behaviourand was added to moq in January 2009 (version 3.0.203.1).
If moq finds an IEnumerable, it uses SequenceEqual to compare the actual argument and the argument used in the setup, otherwise it just uses Equals.
Here's the relevant bit of code:
internal class ConstantMatcher : IMatcher
{
...
public bool Matches(object value)
{
if (object.Equals(constantValue, value))
{
return true;
}
if (this.constantValue is IEnumerable && value is IEnumerable)
{
return this.MatchesEnumerable(value);
}
return false;
}
private bool MatchesEnumerable(object value)
{
var constValues = (IEnumerable)constantValue;
var values = (IEnumerable)value;
return constValues.Cast<object>().SequenceEqual(values.Cast<object>());
}
}

Override ToString() implementation of anonymous objects

var sample = new
{
Time = DateTime.Now,
Name = "Hello"
};
Trace.TraceInformation("{0}", sample);
outputs as
ProcessInvocation86.exe Information: 0 : { Time = 04.11.2012 22:07:52,
Name = Hello }
I'd like different formatting in my application. Is there a way to change the implementation of ToString() for anonymous objects in C#? Maybe some static field per AppDomain or something?
No, you can't do this - ToString, Equals, and GetHashCode have default implementation provided by framework. To override this functionality you should inherit from your anonymous type, which is impossible.
Use String.Format to get desired output.
As far as im aware, there is no way to override the default ToString behaviour.
Might be worthwhile looking at some of the posts from Eric Lippert about anonymous types: http://blogs.msdn.com/b/ericlippert/archive/tags/anonymous+types/
Probably best to create a simple class for this purpose:
e.g.
public class MyClass
{
public DateTime Time { get; set; }
public string Name { get; set; }
public override string ToString()
{
return string.Format("Time = {0}. Name = {1}.", Time, Name);
}
}
I know, some guys will actually punch me for such a solution and I agree, that you shouldn't use it in production. If you have specific functionality for some bunch of data - this should definitely go to separate class. But for minor issues, you can use a little of reflection like this to write some custom formatter (I repeat, I'm not suggesting to use it in production ):
private string FormatProperties<T> (T obj)
{
string result = "";
var type = typeof(T);
foreach (var prop in type.GetProperties())
{
result += string.Format("{0}:{1}\r\n", prop.Name, prop.GetValue(obj));
}
return result;
}
Then the call
var anon = new {Name = "Ilya", Surname = "Ivanov"};
Console.WriteLine (FormatProperties(anon));
Will result in printed
Name:Ilya
Surname:Ivanov
And then you can cache types for performance benefits and get into another types of troubles.
this isn't exactly ideal.. but you could create an extension method that takes a function that does the formatting.
Following example formatted for LinqPad
EXAMPLE
void Main()
{
var sample = new
{
Time = DateTime.Now,
Name = "Hello",
};
sample.ToAnonString(()=>sample.Name).Dump();
}
public static class ovs{
public static string ToAnonString(this object o,Func<string> exp){
return exp();
}
}

Pass parameters to constructor, when initializing a lazy instance

public class myClass
{
public myClass(String InstanceName)
{
Name = InstanceName;
}
public String Name { get; set; }
}
// Now using myClass lazily I have:
Lazy<myClass> myLazy;
Console.WriteLine(myLazy.Value.Name);
My question is how to pass InstanceName to myClass constructor when we are using a lazy instance ?
Try this:
Lazy<myClass> myLazy = new Lazy<myClass>(() => new myClass(InstanceName));
Remember that the expression is evaluated lazily, so if you change the value of the variable InstanceName before the constructor is called it might not do what you expect.
Lazy has two ways to initialize. The first is using T's default ctor (parameterless)
the second is accepting an Func that has customer initialization logic. you should use the second overload as mentioned here
http://msdn.microsoft.com/en-us/library/dd642329.aspx
You can't, Lazy<T> requires a parameterless constructor. You could use the Lazy<T>(Func<T>) constructor though, with a method that initializes the class.
I struggled with this today and in my use case, I didn't want to use parameters from the constructor. I wanted to use parameters from a consumer of my type. Something like this.
public class MyClass
{
private readonly Lazy< **something** > names;
public MyClass()
{
// Initialize the names here
this.names = new Lazy< **something ** > (() =>
{
// do something here based off of a birth date
// AND
// only do it one time (the first time the this.names.Value is called
});
}
public string GetName(DateTime birthDate)
{
return this.names.Value(birthDate);
}
}
I tried using the Lazy(Func) constructor as mentioned by #ibrahim's answer like this.
public class MyClass
{
private readonly Lazy<Func<DateTime,string>> names;
public MyClass()
{
// Initialize the names here
this.names = new Lazy<Func<DateTime, string>>(birthDate =>
{
this.getNamesAndBirthDates().ToDictionary(x => x.Item2, x => x.Item1).TryGetValue(birthDate, out var name);
return name;
});
}
public string GetName(DateTime birthDate)
{
return this.names.Value(birthDate);
}
private IEnumerable<(string, DateTime)> getNamesAndBirthDates()
{
// returns a list fetching it from where ever...
return new[]
{
("name", new DateTime(2000, 1, 1))
};
}
}
This didn't work the way I wanted. I wanted the following:
this.getNamesAndBirthDates().ToDictionary(x => x.Item2, x => x.Item1).TryGetValue(birthDate, out var name);
return name;
to only be called one time per unique birth date value. It makes sense as to why it works the way it does, it's just that I didn't want it to work like that. I ended up with the following code that allowed me to do all of the things I wanted.
public class MyClass
{
private readonly Func<DateTime, string> names;
public MyClass()
{
var container = new Dictionary<DateTime, Lazy<string>>();
// Initialize the names here
this.names = birthDate =>
{
if (container.TryGetValue(birthDate, out var result)) return result.Value;
result = new Lazy<string>(() =>
{
// This code is only executed one time per unique birth date
this.getNamesAndBirthDates().ToDictionary(x => x.Item2, x => x.Item1).TryGetValue(birthDate, out var name);
return name;
});
container.Add(birthDate, result);
return result.Value;
};
}
public string GetName(DateTime birthDate)
{
return this.names(birthDate);
}
private IEnumerable<(string, DateTime)> getNamesAndBirthDates()
{
// returns a list fetching it from where ever...
return new[]
{
("name", new DateTime(2000, 1, 1))
};
}
}
Now I get everything I wanted.
Execute a Lazy func with consumer parameters - not constructor parameters.
Ensure that the Lazy build up code is only done one time per unique parameter value (in this case birth date).
Execute the build up lazily. Meaning only execute the build up when needed (but have it defined in the constructor of the MyClass type)
This code is just an simple example. It doesn't really make sense to do what I'm doing here in real life. But I've used this concept in production and it worked good.

Categories

Resources