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.
Related
I'm learning and currently trying to write a simple multiple choice quiz. I am not allowed to write it simply with if else statements.
I'm trying to make it so that it asks the question the user inputs the answer (that's all fine) but a separate class works out if it's right or not.
Where my code has the array with a blank index I'm wondering how after the first iteration it would check the next index when that method was called upon again in the next question. I tried to use a for each but then get an error regarding not every path has an outcome.
private static readonly string[] answerArray = new string[5] { "C", "A", "B", "A", "C" };
public static bool AnswerCalc(string ain)
{
if (ain == answerArray[])
{
Console.WriteLine("Correct");
return true;
}
else
{
Console.WriteLine("Sorry that's wrong");
return false;
}
}
I had it as a bool as that's how I was keeping score.
If you want to check if an array contains an item simply:
var aInArr = arr.Contains("A");
This is a LINQ expression built into .net.
One way to implement this as a non-extension method targeting array is like so:
public bool Contains(string[] arr, string inputStr)
{
for(int i = 0; i < arr.Length; i++)
{
if (item == inputStr[i])
return true;
}
return false;
}
Once it runs out of items in the array it will break out of the for loop curly brackets and return false.
Note: for brevity I reduced the result but the .net way to do this is to use an IEnumerable on a generic input instead of an array and use foreach instead of for.
public static Contains<T>(this IEnumerable<T> input, T compared)
{
foreach (var item in input)
{
if (item == compared)
return true;
}
return false;
}
If you store an integer in the class containing this method, you can increment it at the end of the function call (make sure you reset it to 0 when it reaches the length of the array). Use this integer between the square braces like so: if (ain == answerArray[intValue])
If you need more help then let me know and I'll be happy to update this with more information.
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'm working on a SignalR WPF application. Im sending messages from Windows Phone. I want to find specific item in that collection.
My view model:
public ViewModel()
{
Messages = new ObservableCollection<string>();
_connection = new HubConnection("http://localhost:49671/");
_dataHub = _connection.CreateHubProxy("dataHub");
}
public ObservableCollection<string> Messages
{
get { return _messages; }
set
{
if (Equals(value, _messages)) return;
_messages = value;
OnPropertyChanged("Messages");
}
}
public async Task Login(string roomName, string userName)
{
_userName = userName;
_roomName = roomName;
await _connection.Start();
await _dataHub.Invoke("JoinRoom", new object[] { _roomName, _userName });
_dataHub.Subscribe("ReceiveMessage").Received += list =>
Dispatcher.CurrentDispatcher.BeginInvoke((Action)(() =>
Messages.Add(list[0].ToString())));
}
Codes that I tried to search
var asd2 = App.MainViewModel.Messages.Where(a => a.Contains("on"));
var on = App.MainViewModel.Messages.IndexOf(App.MainViewModel.Messages.Where(x => x == "on").FirstOrDefault());
List<string> asd = App.MainViewModel.Messages.Where(a => a.Contains("on")).ToList();
var q = App.MainViewModel.Messages.IndexOf(App.MainViewModel.Messages.Contains("on").ToString());
nothing worked for now. Please help .
Edit: The answer on this site didnt work for me. I dont know where the problem is
Attempt no 1 should work fine, as long as the target string has the same casing (UPPERCASE vs lowercase). This search is case sensitive meaning it will NOT find "On", "oN" or "ON" bacause they have different casings. To make case insensitive search, you can use IndexOf instead, which takes a StringComparison parameter:
var asd2 = App.MainViewModel.Messages.Where(a => a.IndexOf("on", StringComparison.CurrentCultureIgnoreCase) >= 0);
Attempt no 2 finds the start position of the first string which matches "on" (again - case sensitive)... This doesn't make any sense really, since any string which exactly matches "on", will always start a position 0.
Attempt no 3 does the same as attempt no 1, but converts the result to a list (Where returns IEnumerable)
Attempt no 4 essentially tries to find the starting position of either "true" or "false". The Contains method will return true if the string "on" (again only exact match) is found, and that result is converted to a string and passed to the IndexOf.
UPDATE
Where returns an IEnumerable (with all the matches found). If you only need to check if "on" exists, you can use Any:
bool containsOn = App.MainViewModel.Messages.Any(a => a.IndexOf("on", StringComparison.CurrentCultureIgnoreCase) >= 0);
If you are dealing with cases and don't have an async issue, the code below works.
Check out this post
Extension method, taken from the post basicly.
public static class StringExt
{
public static bool Contains(this string source, string toCheck, StringComparison comp)
{
return source.IndexOf(toCheck, comp) >= 0;
}
}
Note that the extension method above will find everything with "on" in it regardless of case, add or modify methods to suit your needs, makes life easier :) I personally love them!
Then for searching
// get first message with on in it
var res = App.MainViewModel.Messages.FirstOrDefault(m => m.Contains("on", StringComparison.OrdinalIgnoreCase));
// get everything with on in it
var res2 = App.MainViewModel.Messages.Where(m => m.Contains("on", StringComparison.OrdinalIgnoreCase));
Hope it helps, and was what you were after
Cheers
Stian
Sending messages as strings like this is really not ideal. Maybe have a look at this library that uses the Event aggregation pattern?
Disclaimer: I'm the author
https://github.com/AndersMalmgren/SignalR.EventAggregatorProxy/wiki
Can someone please explain me what I am missing here. Based on my basic understanding linq result will be calculated when the result will be used and I can see that in following code.
static void Main(string[] args)
{
Action<IEnumerable<int>> print = (x) =>
{
foreach (int i in x)
{
Console.WriteLine(i);
}
};
int[] arr = { 1, 2, 3, 4, 5 };
int cutoff = 1;
IEnumerable<int> result = arr.Where(x => x < cutoff);
Console.WriteLine("First Print");
cutoff = 3;
print(result);
Console.WriteLine("Second Print");
cutoff = 4;
print(result);
Console.Read();
}
Output:
First Print
1
2
Second Print
1
2
3
Now I changed the
arr.Where(x => x < cutoff);
to
IEnumerable<int> result = arr.Take(cutoff);
and the output is as follow.
First Print
1
Second Print
1
Why with Take, it does not use the current value of the variable?
The behavior your seeing comes from the different way in which the arguments to the LINQ functions are evaluated. The Where method recieves a lambda which captures the value cutoff by reference. It is evaluated on demand and hence sees the value of cutoff at that time.
The Take method (and similar methods like Skip) take an int parameter and hence cutoff is passed by value. The value used is the value of cutoff at the moment the Take method is called, not when the query is evaluated
Note: The term late binding here is a bit incorrect. Late binding generally refers to the process where the members an expression binds to are determined at runtime vs. compile time. In C# you'd accomplish this with dynamic or reflection. The behavior of LINQ to evaluate it's parts on demand is known as delayed execution.
There's a few different things getting confused here.
Late-binding: This is where the meaning of code is determined after it was compiled. For example, x.DoStuff() is early-bound if the compiler checks that objects of x's type have a DoStuff() method (considering extension methods and default arguments too) and then produces the call to it in the code it outputs, or fails with a compiler error otherwise. It is late-bound if the search for the DoStuff() method is done at run-time and throws a run-time exception if there was no DoStuff() method. There are pros and cons to each, and C# is normally early-bound but has support for late-binding (most simply through dynamic but the more convoluted approaches involving reflection also count).
Delayed execution: Strictly speaking, all Linq methods immediately produce a result. However, that result is an object which stores a reference to an enumerable object (often the result of the previous Linq method) which it will process in an appropriate manner when it is itself enumerated. For example, we can write our own Take method as:
private static IEnumerable<T> TakeHelper<T>(IEnumerable<T> source, int number)
{
foreach(T item in source)
{
yield return item;
if(--number == 0)
yield break;
}
}
public static IEnumerable<T> Take<T>(this IEnumerable<T> source, int number)
{
if(source == null)
throw new ArgumentNullException();
if(number < 0)
throw new ArgumentOutOfRangeException();
if(number == 0)
return Enumerable.Empty<T>();
return TakeHelper(source, number);
}
Now, when we use it:
var taken4 = someEnumerable.Take(4);//taken4 has a value, so we've already done
//something. If it was going to throw
//an argument exception it would have done so
//by now.
var firstTaken = taken4.First();//only now does the object in taken4
//do the further processing that iterates
//through someEnumerable.
Captured variables: Normally when we make use of a variable, we make use of how its current state:
int i = 2;
string s = "abc";
Console.WriteLine(i);
Console.WriteLine(s);
i = 3;
s = "xyz";
It's pretty intuitive that this prints 2 and abc and not 3 and xyz. In anonymous functions and lambda expressions though, when we make use of a variable we are "capturing" it as a variable, and so we will end up using the value it has when the delegate is invoked:
int i = 2;
string s = "abc";
Action λ = () =>
{
Console.WriteLine(i);
Console.WriteLine(s);
};
i = 3;
s = "xyz";
λ();
Creating the λ doesn't use the values of i and s, but creates a set of instructions as to what to do with i and s when λ is invoked. Only when that happens are the values of i and s used.
Putting it all together: In none of your cases do you have any late-binding. That is irrelevant to your question.
In both you have delayed execution. Both the call to Take and the call to Where return enumerable objects which will act upon arr when they are enumerated.
In only one do you have a captured variable. The call to Take passes an integer directly to Take and Take makes use of that value. The call to Where passes a Func<int, bool> created from a lambda expression, and that lambda expression captures an int variable. Where knows nothing of this capture, but the Func does.
That's the reason the two behave so differently in how they treat cutoff.
Take doesn't take a lambda, but an integer, as such it can't change when you change the original variable.
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).