In the code below, I have the "howmanystringasync" method which is calling two others methods. Those two are faked.
The second fake's return does not work because of the ".ToList()".
I usually returns IEnumerable because in some case I want to restrict the caller actions. And sometimes, I ask in input directly a List so a method can do what a List has to offer.
How can I make the test below works ?
var result = await f.AccessTheWebAsync2(web.ToList());
using FakeItEasy;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Xunit;
namespace api.tests.unit
{
public class SpecialString
{
public int IntProperty { get; set; }
public string StringProperty { get; set; }
public override bool Equals(object obj)
{
if (obj == null || GetType() != obj.GetType())
{
return false;
}
if (ReferenceEquals(this, obj)) return true;
return Equals(obj as SpecialString);
}
public bool Equals(SpecialString other)
{
if (other == null) return false;
return (this.IntProperty == other.IntProperty) && (this.StringProperty == other.StringProperty);
}
}
public interface IFoo
{
Task<IEnumerable<SpecialString>> AccessTheWebAsync(int a, int b);
Task<int> AccessTheWebAsync2(List<SpecialString> integers);
}
public class Foo : IFoo
{
public async Task<IEnumerable<SpecialString>> AccessTheWebAsync(int a, int b)
{
HttpClient client = new HttpClient();
string result = await client.GetStringAsync("https://msdn.microsoft.com");
var results = new List<SpecialString> {
new SpecialString{
IntProperty = 1,
StringProperty = "stringprop1"
},
new SpecialString{
IntProperty =2,
StringProperty = "stringprop2"
}
};
return results;
}
public async Task<int> AccessTheWebAsync2(List<SpecialString> specialstrings)
{
HttpClient client = new HttpClient();
string result = await client.GetStringAsync("https://msdn.microsoft.com");
var results = new List<SpecialString> {
new SpecialString{
IntProperty = 1,
StringProperty = "stringprop1"
},
new SpecialString{
IntProperty =2,
StringProperty = "stringprop2"
}
};
return results.Count();
}
}
public class BiggerFoo
{
private readonly IFoo f;
public BiggerFoo(IFoo f)
{
this.f = f;
}
public async Task<int> howManyStringsAsync(int a, int b)
{
var web = await f.AccessTheWebAsync(a, b);
var result = await f.AccessTheWebAsync2(web.ToList());
return result;
}
}
public class FooTest
{
[Fact]
public void testasyncmethod()
{
var fakeFoo = A.Fake<IFoo>();
IEnumerable<SpecialString> result = new List<SpecialString> {
new SpecialString{
IntProperty = 1,
StringProperty = "fakestringprop1"
},
new SpecialString{
IntProperty =2,
StringProperty = "fakestringprop2"
}
};
A.CallTo(() => fakeFoo.AccessTheWebAsync(1, 2)).Returns<Task<IEnumerable<SpecialString>>>(Task.FromResult(result));
A.CallTo(() => fakeFoo.AccessTheWebAsync2(result.ToList())).Returns<Task<int>>(Task.FromResult(5));
var bFoo = new BiggerFoo(fakeFoo);
bFoo.howManyStringsAsync(1, 2);
}
}
}
If I read things right, the problem is that you configure AccessTheWebAsync2 to return 5 when given a particular List<SpecialString>, but in your test, the method is called with a different List<SpecialString>, and List<SpecialString>.Equals only does reference equality, so you're getting 0 back. If you want to make sure 5 is returned when a List<SpecialString> containing your desired SpecialStrings is passed to AccessTheWebAsync2, you'll need to adjust the constraint. It looks like SpecialString doesn't have a value-based Equals either, so you could consider examining the elements' properties:
A.CallTo(() => fakeFoo.AccessTheWebAsync(1, 2)).Returns(result);
A.CallTo(() => fakeFoo.AccessTheWebAsync2(
A<List<SpecialString>>.That.Matches(l =>
l.Count == 2 && l[0].IntProperty == 1 && l[1].StringProperty == "fakestringprop2")))
.Returns(5);
Or, if you really don't care about the input value, something like
A.CallTo(() => fakeFoo.AccessTheWebAsync2(A<List<SpecialString>>._))
.Returns(5);
Update: now that you've added SpecialString.Equals, using the list's values as a call matching constraint is easier:
A.CallTo(() => fakeFoo.AccessTheWebAsync2(
A<List<SpecialString>>.That.IsSameSequenceAs(result)))
.Returns(5);
If you haven't already, do check out all of the argument constraints that FakeItEasy provides.
Related
I am doing unit testing, and basically want to check that the data that 2 objects hold is the same
Assert.AreEqual(object1, object2);
Assert.IsTrue(object1.Equals(object2)); //this of course doesn't work
I am searching for the C# equivalent of assertJ
Assert.That(object1).isEqualToComparingFieldByField(object2)
You could either use records (c# 9 +) or you have to override the Equals method (if you have access and you can change the objects that you're working with).
Records example:
var point = new Point(3, 4);
var point2 = new Point(3, 4);
var test = point.Equals(point2); //this is true
public record Point(int X, int Y);
with classes:
public class Point
{
public int X { get; }
public int Y { get; }
public override bool Equals(object? obj)
{
if (obj == null)
return false;
return obj is Point point && (point.X == X && point.Y == Y);
}
public override int GetHashCode()
{
return HashCode.Combine(X, Y);
}
}
if you are not allowed to touch the implementation, then you could use serialization and compare the strings:
var obj1Str = JsonConvert.SerializeObject(object1);
var obj2Str = JsonConvert.SerializeObject(object2);
Assert.Equal(obj1Str, obj2Str);
using Newtonsoft.Json nuget
C# classes are reference equality, which means that variables are the same using the standard Equals and == if they point to the same object, you could override that behaivour, but it may break something now or in the future.
Or, you could switch to using a construct that's value equality by default, which structs as well as record classes are. If you can't (or don't want to) do that you can implement a value equals "helper" method yourself. I would not recommend overriding the Equals method or the == operator, as that can (and most likely will) lead to errors in the future instead I recommend you write your own ValueEquals method or extension method, something along the lines of
class Foo
{
public int Count {get; set;}
public string Message {get; set;}
}
public static bool ValueEquals(this Foo self, Foo other)
{
return self.Count == other.Count && self.Message == other.Message;
}
public void MyTest()
{
// Arrange and Act
...
// Assert
Assert.IsTrue(myFoo1.ValueEquals(myFoo2));
}
Depending on whether or not you can/ want to add a ValueEquals to your Foo class you can decide on doing it with an extension method or a normal method.
You could also implement a IEqualityComparer<T> like
public class FooValueEqualityComparer : IEqualityComparer<Foo>
{
public bool Equals(Foo foo1, Foo foo2)
{
return foo1.Count == foo2.Count &&
foo1.Message == foo2.Message;
}
public int GetHashCode(Foo foo)
{
return foo.GetHashCode();
}
}
// Use it
public void MyTest()
{
// Arrange and Act
...
// Assert
Assert.IsTrue(new FooEqualityComparer().Equals(myFoo1, myFoo2));
}
Or, you could write a generic ValueEquals that works for all^* classes using Reflection:
public static class ValueEqualityComparer
{
public static bool ValueEquals<T>(this T self, T other) where T : class
{
var type = self.GetType();
if (type == typeof(string))
return self.Equals(other);
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var property in properties)
{
var selfValue = property.GetValue(self);
var otherValue = property.GetValue(other);
// String is special, it's not primitive but is value equality like primitives
if (property.PropertyType.IsPrimitive || property.PropertyType == typeof(string))
{
if (!selfValue.Equals(otherValue))
return false;
}
// If the property is a List value equals each member
// Maybe find another type that allows indexing and is less restrictive
else if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
{
var selfList = ((IEnumerable)property.GetValue(self)).Cast<object>();
var otherList = ((IEnumerable)property.GetValue(other)).Cast<object>();
try
{
// Using EquiZip from MoreLinq: https://github.com/morelinq/MoreLINQ/blob/master/MoreLinq/EquiZip.cs
foreach (var element in selfList.EquiZip(otherList, (selfItem, otherItem) => new { selfItem, otherItem }))
{
if (!ValueEquals(element.selfItem, element.otherItem))
return false;
}
}
catch (InvalidOperationException)
{
// MoreLINQ throws a InvalidOperationException if our two enumerables aren't the same length
return false;
}
}
else
{
if (!ValueEquals(selfValue, otherValue))
return false;
}
}
return true;
}
}
This implementation is by no means perfect, and should honestly only be used for UnitTests and also should be thoroughly tested itself. You can see my tests as a dotnetfiddle here
Or you could do it "dirty" and serialize the objects to a string and compare those values.
I have a code like this:
using System;
using System.Collections.Generic;
using System.Linq;
public interface IMyString
{
string Id {get;set;}
};
public class MyString : IMyString
{
public string Id {get;set;}
}
public static class Extensions
{
public static IEnumerable<IMyString> WithId(this IEnumerable<IMyString> source, string id)
{
return source.Where(x => x.Id == id);
}
}
public class Program
{
private static List<T> GetMyStrings<T>(string key, List<T> input)
where T: IMyString
{
return input.WithId(key).ToList();
}
public static void Main()
{
var foo = new List<MyString>{ new MyString { Id = "yes"}, new MyString { Id = "no" } };
var result = GetMyStrings("yes", foo);
var result2 = foo.WithId("no");
Console.WriteLine(result2);
}
}
Why does input.WithId(key).ToList() cause a syntax error, while foo.WithId("no") is OK? Is there a way to make the method GetMyStrings work?
Without the context of your code it is a little difficult to help too much, but your type constraints for the two methods are different. You have two options:
Option 1:
public static class Extensions
{
public static IEnumerable<T> WithId<T>(this IEnumerable<T> source, string id) where T: IMyString
{
return source.Where(x => x.Id == id);
}
}
Option 2:
private static List<IMyString> GetMyStrings(string key, List<IMyString> input)
{
return input.WithId(key).ToList();
}
public static void Main()
{
var foo = new List<IMyString>{ new MyString { Id = "yes"}, new MyString { Id = "no" } };
var result = GetMyStrings("yes", foo);
var result2 = foo.WithId("no");
Console.WriteLine(result2);
}
Here is a dotnetfiddle with the second option as a working piece of code:
public static IEnumerable<T> WithId<T>(this IEnumerable<T> source, string id)
where T : IMyString
{
return source.Where(x => x.Id == id);
}
So I am using Simple.Mocking to Mock some interfaces on my tests. Some methods receive custom objects
public class MyObj
{
public int Attr { get; set; }
public override bool Equals(object obj)
{
return Equals(obj as MyObj);
}
public override int GetHashCode()
{
return Attr;
}
private bool Equals(MyObj myObj)
{
return Attr == myObj.Attr;
}
}
public interface IFoo
{
void Show(MyObj o);
}
public class ObjUnderTest
{
public ObjUnderTest(IFoo foo)
{
var o = new MyObj{ Attr = 1; };
foo.Show(o);
}
}
[TestClass]
public class TestClasse
{
[TestMethod]
public void TestShow()
{
var foo = Mock.Interface<IFoo>();
var myObj = new MyObj { Attr = 1 };
Expect.Once.MethodCall(() => foo.Show(myObj));
var objectUnderTest = new ObjUnderTest(foo);
AssertExpectations.IsMetFor(foo);
}
}
The problems is that test fails always, even when Show is called with a object with Attrequals to 1. It only pass if I write the expect like this:
Expect.Once.MethodCall(()=> foo.Show(Any<MyObj>.Value));
Which is not what I need. I know it fails because those are different objects but I have tried overriding MyObj Equals and GetHashCode with no success.
Any Ideas?
If the desired outcome is to validate the input you can try specifying exptectation with a predicate
Expect.Once.MethodCall(()=> foo.Show(Any<MyObj>.Value.Matching(obj => obj.Attr == 1)));
Source: project readme on Github - Using "wildcard" parameter values
[TestClass]
public class TestClasse {
[TestMethod]
public void TestShow() {
//Arrange
var foo = Mock.Interface<IFoo>();
Expect.Once.MethodCall(()=> foo.Show(Any<MyObj>.Value.Matching(obj => obj.Attr == 1)));
//Act
var objectUnderTest = new ObjUnderTest(foo);
//Assert
AssertExpectations.IsMetFor(foo);
}
}
I am trying to verify a complex collection was passed into a method.
I can't seem to think of how to write the lambda to test against.
Here is a simplified version of what I am trying to do:
var parameters = new List<NotificationParameter>()
{
new NotificationParameter()
{
Key = "photoShootId",
Value = 1,
},
};
message.Verify(m => m.Load(It.Is<List<NotificationParameter>>(
p => // ??? Ensure that 'p' and 'parameters' have
// the same elements and values for 'Key' and 'Value'
)
));
Just passing in parameters to verify fails the test, so I would like to test against the Key and Value properties.
p above is of type List<NotificationParameter>.
You may use .All which execute an expression on each of the element and return true if all returns true, so for your particular case we could write the condition as
message.Verify(m => m.Load(It.Is<List<NotificationParameter>>(
pList => pList.All(p => parameters.Any(pTest => p.Key == pTest.Key && p.Value == pTest.Value))
)
));
Other method is to use IEqualityComparer & SequenceEqual
eg
class NotificationParameterComparer : IEqualityComparer<NotificationParameter>
{
public bool Equals(NotificationParameter x, NotificationParameter y)
{
if(x==null || y == null)
return false;
return x.Key == y.Key && x.Value == y.Value
}
public int GetHashCode(NotificationParameter parameter)
{
if (Object.ReferenceEquals(parameter, null)) return 0;
int hashKey = parameter.Key == null ? 0 : parameter.Key.GetHashCode();
int hashValue = parameter.Value.GetHashCode();
return hashKey ^ hashValue;
}
}
then use it as
message.Verify(m => m.Load(It.Is<List<NotificationParameter>>(
pList => pList.SequenceEqual(parameters, new NotificationParameterComparer())
)
));
To match the contents of a list one can use SequenceEquals() and an IEqualityComparer<>
public class NotificationParameter {
public NotificationParameter(string key, int value) {
Key = key;
Value = value;
}
public string Key { get; set; }
public int Value { get; set; }
}
public interface IService {
void Load(IEnumerable<NotificationParameter> parameters);
}
public class ClientClass {
private readonly IService _service;
public ClientClass(IService service) {
_service = service;
}
public void Run(IEnumerable<NotificationParameter> parameters) {
_service.Load(parameters);
}
}
public class NotificationComparer : IEqualityComparer<NotificationParameter> {
public bool Equals(NotificationParameter x, NotificationParameter y) {
return Equals(x.Key, y.Key)
&& x.Value.Equals(y.Value);
}
public int GetHashCode(NotificationParameter obj) {
return obj.Value.GetHashCode() ^ obj.Key.GetHashCode();
}
}
private readonly static NotificationComparer Comparer = new NotificationComparer();
[TestMethod]
public void VerifyLoadCompareValues() {
var parameters = new List<NotificationParameter> {
new NotificationParameter("A", 1),
new NotificationParameter("B", 2),
new NotificationParameter("C", 3),
};
var expected = new List<NotificationParameter> {
new NotificationParameter("A", 1),
new NotificationParameter("B", 2),
new NotificationParameter("C", 3),
};
var mockService = new Mock<IService>();
var client = new ClientClass(mockService.Object);
client.Run(parameters);
mockService.Verify(mk => mk.Load(It.Is<IEnumerable<NotificationParameter>>( it=> it.SequenceEqual(expected,Comparer))));
}
If the order will not be the same then a helper method to sort and then compare can be used.
[TestMethod]
public void VerifyLoadCompareDifferentOrder() {
var parameters = new List<NotificationParameter> {
new NotificationParameter("A", 1),
new NotificationParameter("B", 2),
new NotificationParameter("C", 3),
};
var expected = new List<NotificationParameter> {
new NotificationParameter("B", 2),
new NotificationParameter("C", 3),
new NotificationParameter("A", 1),
};
var mockService = new Mock<IService>();
var client = new ClientClass(mockService.Object);
client.Run(parameters);
mockService.Verify(mk => mk.Load(It.Is<IEnumerable<NotificationParameter>>(it => AreSame(expected, it))));
}
private static bool AreSame(
IEnumerable<NotificationParameter> expected,
IEnumerable<NotificationParameter> actual
) {
var ret = expected.OrderBy(e => e.Key).SequenceEqual(actual.OrderBy(a => a.Key), Comparer);
return ret;
}
So I am writing a C# application, using .net/c# 4.0
I have a method which takes in a custom type and a dictionary.
I reuse this for a variety of things but for some reason I cannot think of a way to encapsulate the logic. The problem is this line
if (FastIntParse.FastParse(_dict[_Rule.Key].hourly_data[a].PropertyA) >
_Rule.Value)
In another use it may be
if (FastIntParse.FastParse(_dict[_Rule.Key].hourly_data[a].PropertyB) >
_Rule.Value)
The only thing that varies in the various cases is the Property I am using to compare to the rule value. For some reason I cannot think of a way to reuse it because I don't have the value to pass in to some function since the value is derived IN the function. How can I write a function to abstract away it's need to know which value it needs to derive and pass that information in ie pass it which property it will need to check and not the value of said property.
int a;
for (int z= 0;z<=2;z++)
{
a = (z * z) * 24;
for (; (a%24) <= _Rule.AlertEndTime; a++)
{
if (FastIntParse.FastParse(_dict[_Rule.Key].hourly_data[a].PropertyA) >
_Rule.Value)
{
EnqueueRuleTrigger(_Rule);
break;
}
}
}
I keep rewriting this method inline wherever I need it with the proper property.... this is obviously quite wasteful and any change needs to be made in many places.
Thanks in advance
You can use an Expression and then pull out the property within the method, then use reflection to tie this up to the object within the method
class Program
{
static void Main(string[] args)
{
List<PropertyBag> bags = new List<PropertyBag>()
{
new PropertyBag() {Property1 = 1, Property2 = 2},
new PropertyBag() {Property1 = 3, Property2 = 4}
};
Runme(x => x.Property1, bags);
Runme(x => x.Property2, bags);
Console.ReadLine();
}
public static void Runme(Expression<Func<PropertyBag, int>> expression, List<PropertyBag> bags)
{
var memberExpression = expression.Body as MemberExpression;
var prop = memberExpression.Member as PropertyInfo;
bags.ForEach( bag =>
Console.WriteLine(prop.GetValue(bag, null))
);
}
}
public class PropertyBag
{
public int Property1 { get; set; }
public int Property2 { get; set; }
}
}
to solve the problem with access to different properties and with the use of different boolean-function (<, >, ==) you could use delegates like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace ConsoleApplication1
{
delegate bool CompareFunction(Fii test, Foo item);
class Program
{
static List<Foo> list = new List<Foo>() {
new Foo() { PropertyA = 0, PropertyB = 9 },
new Foo() { PropertyA = 1, PropertyB = 10 }
};
static Fii test = new Fii() { PropertyA = 1 };
static void Main(string[] args)
{
Bar(list, delegate(Fii item1, Foo item2) { return item2.PropertyA < item1.PropertyA; });
Bar(list, delegate(Fii item1, Foo item2) { return item2.PropertyB > item1.PropertyA; });
Bar(list, delegate(Fii item1, Foo item2) { return item2.PropertyA == item1.PropertyA; });
Console.ReadLine();
}
static void Bar(List<Foo> list, CompareFunction cmp)
{
foreach (Foo item in list)
if (cmp(test, item))
Console.WriteLine("true");
else
Console.WriteLine("false");
}
}
class Foo
{
public int PropertyA { get; set; }
public int PropertyB { get; set; }
}
class Fii
{
public int PropertyA { get; set; }
}
}
Make your function take a lambda argument and pass it _ => _.PropertyA, _ => _.PropertyB etc.:
void CheckAndEnqueueRulesByProperty (Func<YourObject, string> propertyGetter)
{
...
if (FastIntParse.FastParse (propertyGetter (
_dict[_Rule.Key].hourly_data[a])) > _Rule.Value)
{
...
}
...
}
If you have many types of objects to check with the same logic, make this function generic.