Compare Strings in Swift unit test - string

How do you test whether two Strings are equal in a Swift unit test? I've tried the == operator but it doesn't recognize it:
import XCTest
#testable import MyProject
class MyProject: XCTestCase {
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testExample() {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
XCTAssertNil(nil, "This test works")
}
func toJSONTest() {
let currentLocation = deviceLocation(timestamp: "2015-11-02 16:32:15 +0000",latitude: "40.2736577695212",longitude: "-111.715408331498")
var MyProjectStatuses = [MyProjectStatus(id: "", currentLocation: currentLocation)]
let json = ""
XCTAssertTrue(json == "")
}
func testPerformanceExample() {
// This is an example of a performance test case.
self.measureBlock {
// Put the code you want to measure the time of here.
}
}
}
And the actual method being tested from MyProject.swift:
func toJSON ()->String{
var json = ""
json = "{\"myproject_status\":"
json = json + "{\"id\":\"" + self.Id + "\""
return json
}
This part:
XCTAssertTrue(json == "")
Throws:
Operator is not a known binary operator

The problem is that toJSONTest is not a test. Change the name to testToJSON.
This works fine on my machine:
func testToJSON() {
let json = ""
XCTAssertTrue(json == "")
}
The test runs, and passes. However, I would probably write it like this:
func testToJSON() {
let json = ""
XCTAssertEqual(json, "", "They are not equal")
}

Although this question is explicitly about how to compare two Strings in a Swift unit test, what's implicit in the question is how to compare two JSON Strings. I just wanted to point out that the right thing to do when comparing two JSON strings is to parse the JSON Strings to a Foundation object with the JSONSerialization class and then to compare the resulting Foundation objects. This approach takes care of the problem of the two JSON Strings having slightly different formatting or fields in a different order. So, for example, it's important that "{\"a\":1,\"b\":2}" and "{\"b\":2,\"a\":1}" are deemed to be equal because they are logically equal.
Here's a Swift function I put together which helps with this comparison:
class JSONAssert {
class func assertEquals(expected: String, actual: String) {
let expectedData = Data(expected.utf8)
let actualData = Data(actual.utf8)
let expectedObject: Any
let actualObject: Any
do {
expectedObject = try JSONSerialization.jsonObject(with: expectedData, options: [])
} catch {
XCTFail("Failed constructing a Foundation object from `expected` (i.e. \(expected)): \(error)")
return
}
do {
actualObject = try JSONSerialization.jsonObject(with: actualData, options: [])
} catch {
XCTFail("Failed constructing a Foundation object from `actual` (i.e. \(actual)): \(error)")
return
}
guard let expectedDictionary = expectedObject as? NSDictionary else {
XCTFail("Failed casting expected object (i.e. \(expectedObject)) to an NSDictionary")
return
}
guard let actualDictionary = actualObject as? NSDictionary else {
XCTFail("Failed casting actual object (i.e. \(actualObject)) to an NSDictionary")
return
}
XCTAssertEqual(expectedDictionary, actualDictionary)
}
}

Related

Generic Template String like in Python in Dart

In python, I often use strings as templates, e.g.
templateUrl = '{host}/api/v3/{container}/{resourceid}'
params = {'host': 'www.api.com', 'container': 'books', 'resourceid': 10}
api.get(templateUrl.format(**params))
This allows for easy base class setup and the like. How can I do the same in dart?
I'm assuming I will need to create a utility function to parse the template and substitute manually but really hoping there is something ready to use.
Perhaps a TemplateString class with a format method that takes a Map of name/value pairs to substitute into the string.
Note: the objective is to have a generic "format" or "interpolation" function that doesn't need to know in advance what tags or names will exist in the template.
Further clarification: the templates themselves are not resolved when they are set up. Specifically, the template is defined in one place in the code and then used in many other places.
Dart does not have a generic template string functionality that would allow you to insert values into your template at runtime.
Dart only allows you to interpolate strings with variables using the $ syntax in strings, e.g. var string = '$domain/api/v3/${actions.get}'. You would need to have all the variables defined in your code beforehand.
However, you can easily create your own implementation.
Implementation
You pretty much explained how to do it in your question yourself: you pass a map and use it to have generic access to the parameters using the [] operator.
To convert the template string into something that is easy to access, I would simply create another List containing fixed components, like /api/v3/ and another Map that holds generic components with their name and their position in the template string.
class TemplateString {
final List<String> fixedComponents;
final Map<int, String> genericComponents;
int totalComponents;
TemplateString(String template)
: fixedComponents = <String>[],
genericComponents = <int, String>{},
totalComponents = 0 {
final List<String> components = template.split('{');
for (String component in components) {
if (component == '') continue; // If the template starts with "{", skip the first element.
final split = component.split('}');
if (split.length != 1) {
// The condition allows for template strings without parameters.
genericComponents[totalComponents] = split.first;
totalComponents++;
}
if (split.last != '') {
fixedComponents.add(split.last);
totalComponents++;
}
}
}
String format(Map<String, dynamic> params) {
String result = '';
int fixedComponent = 0;
for (int i = 0; i < totalComponents; i++) {
if (genericComponents.containsKey(i)) {
result += '${params[genericComponents[i]]}';
continue;
}
result += fixedComponents[fixedComponent++];
}
return result;
}
}
Here would be an example usage, I hope that the result is what you expected:
main() {
final templateUrl = TemplateString('{host}/api/v3/{container}/{resourceid}');
final params = <String, dynamic>{'host': 'www.api.com', 'container': 'books', 'resourceid': 10};
print(templateUrl.format(params)); // www.api.com/api/v3/books/10
}
Here it is as a Gist.
Here is my solution:
extension StringFormating on String {
String format(List<String> values) {
int index = 0;
return replaceAllMapped(new RegExp(r'{.*?}'), (_) {
final value = values[index];
index++;
return value;
});
}
String formatWithMap(Map<String, String> mappedValues) {
return replaceAllMapped(new RegExp(r'{(.*?)}'), (match) {
final mapped = mappedValues[match[1]];
if (mapped == null)
throw ArgumentError(
'$mappedValues does not contain the key "${match[1]}"');
return mapped;
});
}
}
This gives you a very similar functionality to what python offers:
"Test {} with {}!".format(["it", "foo"]);
"Test {a} with {b}!".formatWithMap({"a": "it", "b": "foo"})
both return "Test it with foo!"
It's even more easy in Dart. Sample code below :
String host = "www.api.com"
String container = "books"
int resourceId = 10
String templateUrl = "$host/api/v3/$container/${resourceId.toString()}"
With the map, you can do as follows :
Map<String, String> params = {'host': 'www.api.com', 'container': 'books', 'resourceid': 10}
String templateUrl = "${params['host']}/api/v3/${params['container']}/${params['resourceId']}"
Note : The above code defines Map as <String, String>. You might want <String, Dynamic> (and use .toString())
Wouldn't it be simplest to just make it a function with named arguments? You could add some input validation if you wanted to.
String templateUrl({String host = "", String container = "", int resourceid = 0 }) {
return "$host/api/v3/$container/$resourceId";
}
void main() {
api.get(templateUrl(host:"www.api.com", container:"books", resourceid:10));
}

Eclipse JDT resolve unknown kind from annotation IMemberValuePair

I need to retrieve the value from an annotation such as this one that uses a string constant:
#Component(property = Constants.SERVICE_RANKING + ":Integer=10")
public class NyServiceImpl implements MyService {
But I am getting a kind of K_UNKNOWN and the doc says "the value is an expression that would need to be further analyzed to determine its kind". My question then is how do I perform this analysis? I could even manage to accept getting the plain source text value in this case.
The other answer looks basically OK, but let me suggest a way to avoid using the internal class org.eclipse.jdt.internal.core.Annotation and its method findNode():
ISourceRange range = annotation.getSourceRange();
ASTNode annNode = org.eclipse.jdt.core.dom.NodeFinder.perform(cu, range);
From here on you should be safe, using DOM API throughout.
Googling differently I found a way to resolve the expression. Still open to other suggestions if any. For those who might be interested, here is a snippet of code:
if (valueKind == IMemberValuePair.K_UNKNOWN) {
Annotation ann = (Annotation)annotation;
CompilationUnit cu = getAST(ann.getCompilationUnit());
ASTNode annNode = ann.findNode(cu);
NormalAnnotation na = (NormalAnnotation)annNode;
List<?> naValues = na.values();
Optional<?> optMvp = naValues.stream()
.filter(val-> ((MemberValuePair)val).getName().getIdentifier().equals(PROPERTY))
.findAny();
if (optMvp.isPresent()) {
MemberValuePair pair = (MemberValuePair)optMvp.get();
if (pair.getValue() instanceof ArrayInitializer) {
ArrayInitializer ai = (ArrayInitializer)pair.getValue();
for (Object exprObj : ai.expressions()) {
Expression expr = (Expression)exprObj;
String propValue = (String)expr.resolveConstantExpressionValue();
if (propValue.startsWith(Constants.SERVICE_RANKING)) {
return true;
}
}
}
else {
Expression expr = pair.getValue();
String propValue = (String)expr.resolveConstantExpressionValue();
if (propValue.startsWith(Constants.SERVICE_RANKING)) {
return true;
}
}
}
//report error
}
private CompilationUnit getAST(ICompilationUnit compUnit) {
final ASTParser parser = ASTParser.newParser(AST.JLS8);
parser.setKind(ASTParser.K_COMPILATION_UNIT);
parser.setSource(compUnit);
parser.setResolveBindings(true); // we need bindings later on
CompilationUnit unit = (CompilationUnit)parser.createAST(null);
return unit;
}

How to handle NSInvalidArgumentException thrown when fetching aggregate data from empty persistent store

I am trying to fetch data grouped by a given column. It works well when I have data. I want to handle the case when I have no data, because it raises an NS error that I could not catch in swift do catch block. I've seen the answers on creating an ObjC wrapper but I it does not apply to my case because I need to return an Array of String.
let request = self.fetchRequest()
request.propertiesToGroupBy = [attribute]
request.propertiesToFetch = [attribute]
request.resultType = NSFetchRequestResultType.dictionaryResultType
request.returnsDistinctResults = true
if let results = try? context().fetch(request), // raises exception in testing, but runs fine when run on simulator.
let dics = results as? [NSDictionary] {
var resultsArray: [Any] = []
for dic in dics {
if let propValue = dic[attribute] {
resultsArray.append(propValue)
}
}
return resultsArray
}
How might I do this?
It's recommended to wrap Core Data fetch lines always in a do - catch block because on success it returns reliably a non-optional array of the specified return type.
Swift has got a strong type system. Casting to unspecified Any or Foundation type NSDictionary doesn't help the compiler and could cause unnecessary compiler errors.
Assuming both key and value of the dictionary are String cast the dictionary to Swift type [String:String]. To forced unwrap the dictionary is absolutely safe because the Core Data model cannot be changed at runtime.
flatMap returns a non-optional array of all values which are not nil.
var resultsArray = [String]()
do {
let results = try context().fetch(request) as! [[String:String]]
resultsArray = results.flatMap {$0[attribute] as? String}
} catch {
print(error)
}
return resultsArray
Based on this answer
It was not immediately obvious to me that it can be done like this:
let request = self.fetchRequest()
request.propertiesToGroupBy = [attribute]
request.propertiesToFetch = [attribute]
request.resultType = NSFetchRequestResultType.dictionaryResultType
request.returnsDistinctResults = true
var result: [Any] = []
do {
try ObjC.catchException {
let results = try? context().fetch(request)
if let dics = results as? [NSDictionary] {
var resultsArray: [Any] = []
for dic in dics {
if let propValue = dic[attribute] {
resultsArray.append(propValue)
}
}
result = resultsArray
}
}
} catch {}
return result
Ideally I wanted the array to be returned by ObjC.catchException unfortunately I have no solid grasp of ObjC yet. The scope of the result var looks too wide, I welcome any suggestion to improve it.
I wanted to keep everything in swift for uniformity but I guess I am stuck with this solution for now.

(Swift.LazyMapCollection<Swift.Dictionary<Swift.String, Swift.String>(_base:[ ]

The code says that it is perfectly fine and that there are no errors, but when I go to run the simulator, the words will include:
(Swift.LazyMapCollection < Swift.Dictionary < Swift.String, Swift.String > (_base:[ ]
I am trying to create a quote app that displays a quote.
Here is the code for the Import of the Plist:
import Foundation
struct ImportList {
let path: String
init(FileName: String) {
self.path = NSBundle.mainBundle().pathForResource("\(FileName)", ofType:"plist")!
}
var dict: Dictionary<String, String> {
return NSDictionary(contentsOfFile: path)! as! Dictionary
}
var array: Array<AnyObject> {
return [String](arrayLiteral: String(dict.keys) { $0 as String})
}
func count() -> Int {
return array.count
}
}
Thank you.
Don't use arrayLiteral in this case, just use Array():
var array: Array<AnyObject> {
return Array(dict.keys)
}
It safely converts the lazy collection to an actual array.

How to implement finding all positions of one string in another string at the time of typing the string where to search for?

I would like to have the following functionality in my app:
When I type the DNA sequence (string) in the NSTextView window at the same time in my TableView for each enzyme (each of them representing small string) user immediately see the number of found sites (string) corresponding to each enzyme (0 or any number).
I have a function, which I can use to find all possible locations (returning NSRanges array) of string in string. In my case this will be to find in DNA sequence (string) all possible sites (strings NSRanges) corresponding for each enzyme.
Thus, one more time, question is how to implement this function: at the time of typing a string to find all sites (in form of array of NSRanges) in this string and put the numbers found site in table accordingly for each enzyme.
In other words, the function returning NSRanges array for positions of enzymes sites should start automatically.
Update
I am new in cocoa and after suggestions from R Menke (I have putted his code lines below in the code) I have more probably stupid questions. I have one controller class as subclass of NSWindowController. I cannot put code from R Menke to this class (see errors below). And, in my controller class I have my NSTextView where user will type the text as #IBOutlet, should I use this? Should I make another controller file ? Below the code and errors.
import Cocoa
//Error. Multiple inheritance from classes 'NSWindowController' and 'NSViewController'
class AllUnderControl: NSWindowController, NSViewController,NSTextViewDelegate
{
override var windowNibName: String?
{
return "AllUnderControl"
}
override func windowDidLoad() {
super.windowDidLoad()
}
//Error. Instance member 'inputDnaFromUser' cannot be used on type 'AllUnderControl'
var textView = inputDnaFromUser(frame: CGRectZero)
//Error. Method does not override any method from its superclass
override func viewDidLoad() {
textView.delegate = self
}
func textDidChange(notification: NSNotification) {
// trigger your function
}
#IBOutlet var inputDnaFromUser: NSTextView!
Update 2
After reading the description of two controllers: NSWindowController and NSViewController I have made the following changes below. Is it correct for triggering function ?
import Cocoa
class AllUnderControl: NSWindowController, NSTextViewDelegate
{
override var windowNibName: String?
{
return "AllUnderControl"
}
override func windowDidLoad() {
super.windowDidLoad()
inputDnaFromUser.delegate = self
}
func textDidChange(notification: NSNotification) {
// trigger your function
}
#IBOutlet var inputDnaFromUser: NSTextView! = NSTextView(frame: CGRectZero)
If you are asking: "how do I trigger a function when someone types in an NSTextView?"
Implement an NSTextViewDelegate set the delegate of your NSTextView to self and trigger your function inside textDidChange. The NSTextViewDelegate has a set of functions that will be triggered by user interaction. So code inside of them will be executed when the corresponding action happens.
I would suggest using an NSViewController for this and not an NSWindowController. A NSWindowController is used to manage NSViewControllers. NSViewControllers are a better place for things like buttons and textfields.
NSWindowController vs NSViewController
class ViewController: NSViewController, NSTextViewDelegate {
#IBOutlet var inputDnaFromUser: NSTextView!
override func viewDidLoad() {
super.viewDidLoad()
inputDnaFromUser.delegate = self
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
func textDidChange(notification: NSNotification) {
print("editing stuff")
}
}
If you are asking: "how can I find all occurrences of a string in another string?"
This will return an array of ranges. The count of that array is obviously the number of occurrences.
Another option is to use enumerateSubstringsInRange as stated in the answer by #Russel. I just always preferred to write my own loop.
let string = "The sky is blue today, super blue"
let searchString = "blue"
var ranges: [NSRange] = []
var copyString = NSMutableString(string: string)
while copyString.containsString(searchString) {
ranges.append(copyString.rangeOfString(searchString))
guard let lastRange = ranges.last else {
break
}
var replaceString = ""
for _ in 0..<lastRange.length { replaceString += "$" } // unnalowed character
copyString.replaceCharactersInRange(lastRange, withString: replaceString)
}
As suggested in the comments:
A faster method.
let string : NSString = "The sky is blue today, super blue"
let searchString = "blue"
var ranges: [NSRange] = []
var searchRange : NSRange = NSRange(location: 0, length: string.length)
var lastFoundRange : NSRange = string.rangeOfString(searchString, options: NSStringCompareOptions.LiteralSearch, range: searchRange)
while lastFoundRange.location != NSNotFound {
ranges.append(lastFoundRange)
let searchRangeLocation = lastFoundRange.location + lastFoundRange.length
let searchRangeLength = string.length - searchRangeLocation
searchRange = NSRange(location: searchRangeLocation, length: searchRangeLength)
lastFoundRange = string.rangeOfString(searchString, options: NSStringCompareOptions.LiteralSearch, range: searchRange)
}
You will be wanting to do this on a background queue. But this gets tricky quickly. An enzyme can be one kind one moment and change the next. So you will need to do all the work with every character typed.
One possible solution is to cancel each ongoing search when a character is typed. If it finished before typing the next character you get results.
This is an iOS word highlighter I wrote that implements this logic. Except for the use of UIColor everything is pure Foundation. So easy to change it to Cocoa.
You will want to implement the UISearchResultsUpdating protocol to achieve this. It uses a UISearchController (introduced in iOS 8) which has to be added programmatically instead of through the storyboard, but don't worry, it's pretty straight-forward.
The searching is handled using the updateSearchResultsForSearchController delegate method which is called anytime the search bar text is changed. I tried to keep it pretty self-documenting but let me know if you have any questions. Depending on how many enzymes you care to search, this could get inefficient very quickly because you have to search for substring occurrences for each enzyme.
Cheers,
Russell
class YourTableViewController: UITableViewController, UISearchBarDelegate, UISearchResultsUpdating {
// Array of searchable enzymes
var enzymes: [String] = ["...", "...", "..."]
// Dictionary of enzymes mapping to an array of NSRange
var enzymeSites: [String : [NSRange]] = [String : [NSRange]]()
// Search controller
var enzymeSearchController = UISearchController()
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
self.enzymeSearchController = UISearchController(searchResultsController: nil)
self.enzymeSearchController.dimsBackgroundDuringPresentation = true
// This is used for dynamic search results updating while the user types
// Requires UISearchResultsUpdating delegate
self.enzymeSearchController.searchResultsUpdater = self
// Configure the search controller's search bar
self.enzymeSearchController.searchBar.placeholder = "Enter DNA sequence"
self.enzymeSearchController.searchBar.sizeToFit()
self.enzymeSearchController.searchBar.delegate = self
self.definesPresentationContext = true
// Set the search controller to the header of the table
self.tableView.tableHeaderView = self.enzymeSearchController.searchBar
}
// MARK: - Search Logic
func searchEnzymeSites(searchString: String) {
// Search through all of the enzymes
for enzyme in enzymes {
// See logic from here: https://stackoverflow.com/questions/27040924/nsrange-from-swift-range
let nsEnzyme = searchString as NSString
let enzymeRange = NSMakeRange(0, nsEnzyme.length)
nsEnzyme.enumerateSubstringsInRange(enzymeRange, options: NSStringEnumerationOptions.ByWords, { (substring, substringRange, enclosingRange, stop) -> () in
if (substring == enzyme) {
// Update the enzymeSites dictionary by appending to the range array
enzymeSites[enzyme]?.append(substringRange)
}
})
}
}
// MARK: - Search Bar Delegate Methods
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
// Force search if user pushes button
let searchString: String = searchBar.text.lowercaseString
if (searchString != "") {
searchEnzymeSites(searchString)
}
}
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
// Clear any search criteria
searchBar.text = ""
// Force reload of table data from normal data source
}
// MARK: - UISearchResultsUpdating Methods
// This function is used along with UISearchResultsUpdating for dynamic search results processing
// Called anytime the search bar text is changed
func updateSearchResultsForSearchController(searchController: UISearchController) {
let searchString: String = searchController.searchBar.text.lowercaseString
if (searchString != "") {
searchEnzymeSites(searchString)
}
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (self.enzymeSearchController.active) {
return self.enzymeSites.count
} else {
// return whatever your normal data source is
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("userCell") as! UserCell
if (self.enzymeSearchController.active && self.enzymeSites.count > indexPath.row) {
// bind data to the enzymeSites cell
} else {
// bind data from your normal data source
}
return cell
}
// MARK: - UITableViewDelegate
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
if (self.enzymeSearchController.active && self.searchUsers.count > 0) {
// Segue or whatever you want
} else {
// normal data source selection
}
}
}

Resources