I have a unit test to check whether a method returns the correct IEnumerable. The method builds the enumerable using yield return. The class that it is an enumerable of is below:
enum TokenType
{
NUMBER,
COMMAND,
ARITHMETIC,
}
internal class Token
{
public TokenType type { get; set; }
public string text { get; set; }
public static bool operator == (Token lh, Token rh) { return (lh.type == rh.type) && (lh.text == rh.text); }
public static bool operator != (Token lh, Token rh) { return !(lh == rh); }
public override int GetHashCode()
{
return text.GetHashCode() % type.GetHashCode();
}
public override bool Equals(object obj)
{
return this == (Token)obj;
}
}
This is the relevant part of the method:
foreach (var lookup in REGEX_MAPPING)
{
if (lookup.re.IsMatch(s))
{
yield return new Token { type = lookup.type, text = s };
break;
}
}
If I store the result of this method in actual, make another enumerable expected, and compare them like this...
Assert.AreEqual(expected, actual);
..., the assertion fails.
I wrote an extension method for IEnumerable that is similar to Python's zip function (it combines two IEnumerables into a set of pairs) and tried this:
foreach(Token[] t in expected.zip(actual))
{
Assert.AreEqual(t[0], t[1]);
}
It worked! So what is the difference between these two Assert.AreEquals?
Found it:
Assert.IsTrue(expected.SequenceEqual(actual));
Have you considered using the CollectionAssert class instead...considering that it is intended to perform equality checks on collections?
Addendum:
If the 'collections' being compared are enumerations, then simply wrapping them with 'new List<T>(enumeration)' is the easiest way to perform the comparison. Constructing a new list causes some overhead of course, but in the context of a unit test this should not matter too much I hope?
Assert.AreEqual is going to compare the two objects at hand. IEnumerables are types in and of themselves, and provide a mechanism to iterate over some collection...but they are not actually that collection. Your original comparison compared two IEnumerables, which is a valid comparison...but not what you needed. You needed to compare what the two IEnumerables were intended to enumerate.
Here is how I compare two enumerables:
Assert.AreEqual(t1.Count(), t2.Count());
IEnumerator<Token> e1 = t1.GetEnumerator();
IEnumerator<Token> e2 = t2.GetEnumerator();
while (e1.MoveNext() && e2.MoveNext())
{
Assert.AreEqual(e1.Current, e2.Current);
}
I am not sure whether the above is less code than your .Zip method, but it is about as simple as it gets.
I think the simplest and clearest way to assert the equality you want is a combination of the answer by jerryjvl and comment on his post by MEMark - combine CollectionAssert.AreEqual with extension methods:
CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray());
This gives richer error information than the SequenceEqual answer suggested by the OP (it will tell you which element was found that was unexpected). For example:
IEnumerable<string> expected = new List<string> { "a", "b" };
IEnumerable<string> actual = new List<string> { "a", "c" }; // mismatching second element
CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray());
// Helpful failure message!
// CollectionAssert.AreEqual failed. (Element at index 1 do not match.)
Assert.IsTrue(expected.SequenceEqual(actual));
// Mediocre failure message:
// Assert.IsTrue failed.
You'll be really pleased you did it this way if/when your test fails - sometimes you can even know what's wrong without having to break out the debugger - and hey you're doing TDD right, so you write a failing test first, right? ;-)
The error messages get even more helpful if you're using AreEquivalent to test for equivalence (order doesn't matter):
CollectionAssert.AreEquivalent(expected.ToList(), actual.ToList());
// really helpful error message!
// CollectionAssert.AreEquivalent failed. The expected collection contains 1
// occurrence(s) of <b>. The actual collection contains 0 occurrence(s).
Related
I have put together the following method:
public static bool compareTableRow(List<string> expected, int rowNumberOfElemets, IWebDriver driver)
{
List<string> actual = new List<string>
{ };
for (int i = 1; i < rowNumberOfElemets + 1; i++)
{
actual.Add(driver.FindElementHighlight
(By.XPath("//*[#id=\"nefi-content\"]/div[2]/section/div/table/tbody/tr[1]/td[" + i + "]/div/input")).GetAttribute("value"));
}
if (expected.SequenceEqual(actual)) return true;
else
return false;
}
At the moment the 'expected' List is hard-coded. What kind of method variable should I put as an input to be able to call the method and pass the strings I'm trying to compare ("bla1","123", "bla2", "etc", "etc") ?
Even with your implementation you dont need to hardcode the expected argument, it is easy to call your method like this:
compareTableRow(new List<string> {"bla1", "123", "bla2", "etc", "etc"}, 42, driver);
Alternatively, you can use params keyword (https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/params):
public static bool compareTableRow(int rowNumberOfElemets, IWebDriver driver, params string[] expected)
{
...
}
and call it like you described:
compareTableRow(42, driver, "bla1", "123", "bla2", "etc", "etc");
So you have an IWebDriver, which has a notion of a number of rows. You also have an input rowNumberOfElements and a list of expected strings.
You want to check if the first rowNumberOfElements items that you can FindByElementHighlight from the IWebDriver is exactly equal to the list of expected strings.
I see several issues here:
First of all: is the order in the list of expected strings important? So should the first expected string equal the first FindByElementHighLight? According to your code the order is important.
Furthermore: is the list of expected strings long enough? Can it be longer than rowNumberElements, or is it normally exactly long enough, and can you replace the rowNumberElements with the number of elements in the expected list?
If the first element does not match, you know it's useless to check the other elements, because you already know that the return value will be false.
A generic solution
I'll use this to write it as an extension method of IWebDriver. It will be as if you ask the IWebDriver: do the first X elements of you equal the first X elements of this sequence? See extension methods demystified
static bool HasExpectedHighLights(this IWebDriver driver, ICollection<string> expected)
{
// TODO: driver == null? expected == null?
return driver.HasExpectedHighLights(expected, expected.Count);
}
static bool HasExpectedHighLights(this IWebDriver driver,
IEnumerable<string> expected, int nrOfElements)
{
// TODO: driver == null? expected == null? nrOfElements < 0?
your code goes here
}
First we'll show the usage:
List<string> expectedHighLights = ...
IWebDriver driver = ...
var isExpected = driver.HasExpectedHighLights(expectedHighLights);
But is will also work in the middle of some LINQ statement:
IEnumerable<image> images = ...
var imagesWithExpectedHighLights = images
.Where(image => image.Driver.HasExpectedHighLights(expectedHighLights))
.Slect(image => ...);
So now the code. The most important part is that we should stop as soon as you can.
static string FindElementHighLight(this IWebDriver driver, int i)
{
return driver.FindElementHighlight(By.XPath("//*[#id=\" ... etc);
}
static bool HasExpectedHighLights(this IWebDriver driver,
IEnumerable<string> expected, int nrOfElements)
{
// TODO: exception if driver == null? expected == null? nrOfElements < 0?
IEnumerable<string> actualHighLights = driver
.Select( (driver, i) => driver.FindElementHighLight(i)
.Take(nrOfElements);
// note: this has only created the enumerable.
// The sequence has not been enumerated yet
return actualHighLights.SequenceEqual(expected);
// sequence equal will stop as soon as a mismatch has been found
}
So if you find a mismatch at i == 3, then HighLight(4) is not calculated.
The nice thing is that without changing your IWebDriver, you have added functionality to it: FindElementHighLight(int).
This can be used to create the IEnumerable in SequenceEqual, which prevents having to fetch all highlights, even if you detected that the first highlight was not as expected.
Before marking this as duplicate because of its title please consider the following short program:
static void Main()
{
var expected = new List<long[]> { new[] { Convert.ToInt64(1), Convert.ToInt64(999999) } };
var actual = DoSomething();
if (!actual.SequenceEqual(expected)) throw new Exception();
}
static IEnumerable<long[]> DoSomething()
{
yield return new[] { Convert.ToInt64(1), Convert.ToInt64(999999) };
}
I have a method which returns a sequence of arrays of type long. To test it I wrote some test-code similar to that one within Main.
However I get the exception, but I don´t know why. Shouldn´t the expected sequence be comparable to the actually returned one or did I miss anything?
To me it looks as both the method and the epxected contain exactly one single element containing an array of type long, doesn´t it?
EDIT: So how do I achieve to not get the exception meaning to compare the elements within the enumeration to return equality?
The actual problem is the fact that you're comparing two long[], and Enumerable.SequenceEquals will use an ObjectEqualityComparer<Int64[]> (you can see that by examining EqualityComparer<long[]>.Default which is what is being internally used by Enumerable.SequenceEquals), which will compare references of those two arrays, and not the actual values stored inside the array, which obviously aren't the same.
To get around this, you could write a custom EqualityComparer<long[]>:
static void Main()
{
var expected = new List<long[]>
{ new[] { Convert.ToInt64(1), Convert.ToInt64(999999) } };
var actual = DoSomething();
if (!actual.SequenceEqual(expected, new LongArrayComparer()))
throw new Exception();
}
public class LongArrayComparer : EqualityComparer<long[]>
{
public override bool Equals(long[] first, long[] second)
{
return first.SequenceEqual(second);
}
// GetHashCode implementation in the courtesy of #JonSkeet
// from http://stackoverflow.com/questions/7244699/gethashcode-on-byte-array
public override int GetHashCode(long[] arr)
{
unchecked
{
if (array == null)
{
return 0;
}
int hash = 17;
foreach (long element in arr)
{
hash = hash * 31 + element.GetHashCode();
}
return hash;
}
}
}
No, your sequences are not equal!
Lets remove the sequence bit, and just take what is in the first element of each item
var firstExpected = new[] { Convert.ToInt64(1), Convert.ToInt64(999999) };
var firstActual = new[] { Convert.ToInt64(1), Convert.ToInt64(999999) };
Console.WriteLine(firstExpected == firstActual); // writes "false"
The code above is comparing two separate arrays for equality. Equality does not check the contents of arrays it checks the references for equality.
Your code using SequenceEquals is, essentially, doing the same thing. It checks the references in each case of each element in an enumerable.
SequenceEquals tests for the elements within the sequences to be identical. The elements within the enumerations are of type long[], so we actually compare two different arrays (containing the same elements however) against each other which is obsiously done by comparing their references instead of their actual value .
So what we actually check here is this expected[0] == actual[0] instead of expected[0].SequqnceEquals(actual[0])
This is obiosuly returns false as both arrays share different references.
If we flatten the hierarchy using SelectMany we get what we want:
if (!actual.SelectMany(x => x).SequenceEqual(expected.SelectMany(x => x))) throw new Exception();
EDIT:
Based on this approach I found another elegant way to check if all the elements from expected are contained in actual also:
if (!expected.All(x => actual.Any(y => y.SequenceEqual(x)))) throw new Exception();
This will search if for ever sub-list within expected there is a list within actual that is sequentially identical to the current one. This seems much smarter to be as we do not need any custom EqualityComparer and no weird hashcode-implementation.
I am trying to create an elegant and extensible way of querying a dictionary which maps an enum to a set of strings.
So I have this class SearchFragments that has the dictionary in it. I then want a method wherein consumers of this class can simply ask "HasAny" and, this is the bit where I am struggling, simply pass in some query like expression and get the boolean answer back.
public class SearchFragments
{
private readonly IDictionary<SearchFragmentEnum, IEnumerable<string>> _fragments;
public SearchFragments()
{
_fragments = new Dictionary<SearchFragmentEnum, IEnumerable<string>>();
}
public bool HasAny(IEnumerable<SearchFragmentEnum> of)
{
int has = 0;
_fragments.ForEach(x => of.ForEach(y => has += x.Key == y ? 1 : 0));
return has >= 1;
}
}
The problem with the way this currently is, is that consumers of this class now have to construct an IEnumerable<SearchFragmentEnum> which can be quite messy.
What I am looking for is that the consuming code will be able to write something along the lines of:
searchFragments.HasAny(SearchFragmentEnum.Name, SearchFragmentEnum.PhoneNumber)
But where that argument list can vary in size (without me having to write method overloads in the SearchFragments class for every possible combination (such that if new values are added to the SearchFragmentEnum at a future date I won't have to update the class.
You can use params[]
public bool HasAny(params SearchFragmentEnum[] of)
{ ...
Sidenote: you know that LIN(Q) queries should just query a source and never cause any side-effects? But your query does unnecessarily increment the integer:
_fragments.ForEach(x => of.ForEach(y => has += x.Key == y ? 1 : 0));
Instead use this (which is also more efficient and more readable):
return _fragments.Keys.Intersect(of).Any();
An even more efficient alternative to this is Sergey's idea:
return of?.Any(_fragments.ContainsKey) == true;
For variable sized arguments in c# you use the params keyword:
public int HasAny(params SearchFragmentEnum[] of)
The .Net API usually offers a couple of overloads of this for performance reasons; the parameters passed are copied into a new array. Explicitely providing overloads for the most common cases avoids this.
public int HasAny(SearchfragmentEnum of1)
public int HasAny(SearchFragmentEnum of1, SearchFragmentEnum of2)
etc.
Instead of using params you could also consider marking your enum with the [Flags] attribute. Parameters could than be passed like HasAny(SearchFragmentEnum.Name | SearchFragmentEnum.PhoneNumber. Examples abundant on StackOverflow (e.g. Using a bitmask in C#)
Use the params keyword to allow a varying number of arguments. Further, you can simplify your code by looping over the smaller of array. Also, you are using a dictionary that has O(1) key check, so it is uneccessary to have an inner loop:
public bool HasAny(params SearchFragmentEnum[] of)
{
foreach(var o in of) {
if (this._fragments.ContainsKey(o))
return true;
}
return false;
}
or shorter with LINQ
public bool HasAny(params SearchFragmentEnum[] of) {
return of?.Any(_fragments.ContainsKey) ?? false;
}
I've tried searching for the answer on the many other questions but they either don't seem to be relevant or I'm just not knowledgeable enough to know that they are.
My problems is comparing two lists (one of twitter followers the of friends(or who you follow))
This is the code I'm using to gather the lists
var friends = user.GetFriends(500).ToList();
var following = user.GetFollowers(500).ToList();
var result = compareFollowingtoFollowers(friends, following);
foreach(var res in result)
{
lstFollowerChecker.Items.Add(res.ScreenName);
}
And this is my compareFollowingtoFollowers function
private List<T> compareFollowingtoFollowers<T>(List<T> friends, List<T> followers)
{
var results = followers.Except(friends).ToList();
return results;
}
My Problem is it doesn't return what I expect, for example if I ran this against my own account where I say 100 friends and I'm following 112 people, It should return the 12 people that are not following but instead it just seems to return them all.
Am I using the correct function? The other questions I've read lead me to believe so.
Thank you for reading
Bryan
UPDATE
The answers given have been enough to get the cogs in my head ticking again, while the answers are still slightly over my head, I think they were just what I needed to better understand why it was returning what it did, Thank you all.
You need to have a custom comparer to compare your objects and pass that in the .Except method as a second parameter. For example
public class User
{
public int Id { get; set; }
}
public class UserComparer : IEqualityComparer<User>
{
public bool Equals(User x, User y)
{
return x.Id == y.Id;
}
public int GetHashCode(User obj)
{
return obj.Id.GetHashCode();
}
}
You are using the correct function, but your user class does not implement IEquatable<UserClass>. This means that Except uses reference semantics to compare two users, and since they seem to be different objects (even if "equal") they compare unequal. So it thinks that there is no overlap at all between the two lists.
The solution is to properly implement IEquatable<T> to give the class your desired equality semantics (how to do this exactly depends on the properties of that class).
You can do something like:
var result = myFriends.Select(x => yourFriends.All(y => y != x));
It is likely you need to specify how to make comparisons of your types.
According to MSDN:
http://msdn.microsoft.com/en-us/library/bb300779(v=vs.110).aspx
Custom types need to implement equals and gethashcode for comparison to be made.
List1 contains items { A, B } and List2 contains items { A, B, C }.
What I need is to be returned { C } when I use Except Linq extension. Instead I get returned { A, B } and if I flip the lists around in my expression the result is { A, B, C }.
Am I misunderstanding the point of Except? Is there another extension I am not seeing to use?
I have looked through and tried a number of different posts on this matter with no success thus far.
var except = List1.Except(List2); //This is the line I have thus far
EDIT: Yes I was comparing simple objects. I have never used IEqualityComparer, it was interesting to learn about.
Thanks all for the help. The problem was not implementing the comparer. The linked blog post and example below where helpful.
If you are storing reference types in your list, you have to make sure there is a way to compare the objects for equality. Otherwise they will be checked by comparing if they refer to same address.
You can implement IEqualityComparer<T> and send it as a parameter to Except() function. Here's a blog post you may find helpful.
edit: the original blog post link was broken and has been replaced above
So just for completeness...
// Except gives you the items in the first set but not the second
var InList1ButNotList2 = List1.Except(List2);
var InList2ButNotList1 = List2.Except(List1);
// Intersect gives you the items that are common to both lists
var InBothLists = List1.Intersect(List2);
Edit: Since your lists contain objects you need to pass in an IEqualityComparer for your class... Here is what your except will look like with a sample IEqualityComparer based on made up objects... :)
// Except gives you the items in the first set but not the second
var equalityComparer = new MyClassEqualityComparer();
var InList1ButNotList2 = List1.Except(List2, equalityComparer);
var InList2ButNotList1 = List2.Except(List1, equalityComparer);
// Intersect gives you the items that are common to both lists
var InBothLists = List1.Intersect(List2);
public class MyClass
{
public int i;
public int j;
}
class MyClassEqualityComparer : IEqualityComparer<MyClass>
{
public bool Equals(MyClass x, MyClass y)
{
return x.i == y.i &&
x.j == y.j;
}
public int GetHashCode(MyClass obj)
{
unchecked
{
if (obj == null)
return 0;
int hashCode = obj.i.GetHashCode();
hashCode = (hashCode * 397) ^ obj.i.GetHashCode();
return hashCode;
}
}
}
You simply confused the order of arguments. I can see where this confusion arose, because the official documentation isn't as helpful as it could be:
Produces the set difference of two sequences by using the default equality comparer to compare values.
Unless you're versed in set theory, it may not be clear what a set difference actually is—it's not simply what's different between the sets. In reality, Except returns the list of elements in the first set that are not in the second set.
Try this:
var except = List2.Except(List1); // { C }
Writing a custom comparer does seem to solve the problem, but I think https://stackoverflow.com/a/12988312/10042740 is a much more simple and elegant solution.
It overwrites the GetHashCode() and Equals() methods in your object defining class, then the default comparer does its magic without extra code cluttering up the place.
Just for Ref:
I wanted to compare USB Drives connected and available to the system.
So this is the class which implements interface IEqualityComparer
public class DriveInfoEqualityComparer : IEqualityComparer<DriveInfo>
{
public bool Equals(DriveInfo x, DriveInfo y)
{
if (object.ReferenceEquals(x, y))
return true;
if (x == null || y == null)
return false;
// compare with Drive Level
return x.VolumeLabel.Equals(y.VolumeLabel);
}
public int GetHashCode(DriveInfo obj)
{
return obj.VolumeLabel.GetHashCode();
}
}
and you can use it like this
var newDeviceLst = DriveInfo.GetDrives()
.ToList()
.Except(inMemoryDrives, new DriveInfoEqualityComparer())
.ToList();