I'm using server-side Swift, and doing my development in Xcode after doing:
swift package generate-xcodeproj
I have a class that uses Bundle (formerly NSBundle) to load in a .plist file for some settings in the server. It works fine when running in the server itself, but when I create some unit tests for this class, I cannot get access to the directory where the .plist file is located. The relevant snippet of code is:
let bundlePath = Bundle.main.bundlePath as NSString
let plistPath = bundlePath.appendingPathComponent("Test.plist")
plistDict = NSDictionary(contentsOfFile: plistPath)
When I run this in unit XCTests, plistPath is:
/Applications/Xcode-8.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Xcode/Agents/Test.plist
which is not very useful.
One thing that I've noticed is that there are no options for "Host Application:" under the General tab.
Thoughts?
I've not been able to fully answer this, but came up with a work-around for my situation. I'm using the Perfect File class (see https://github.com/PerfectlySoft/Perfect.git), and just dynamically creating the file I need for my XCTest cases in the setUp() method. Fortunately, I have fairly simple needs for file contents. Here's the initial part of my XCTest file:
import XCTest
import SMServerLib
import PerfectLib
class TestPlistDictLoader: XCTestCase {
var plistFileName:String! = "TestPlistDictLoader.plist"
var pathName:String! = "/tmp"
override func setUp() {
super.setUp()
// A bit of a hack, but I can't figure out a way otherwise to access the install directory where the code is running.
// See also http://stackoverflow.com/questions/41340114/server-side-swift-testing-code-that-uses-bundle
// The only downside is that these tests don't test the `init(plistFileNameInBundle filename:String)` constructor that uses the Bundle.
// Write the .plist file to a known location. Use only pure Swift methods.
let plistPath = (pathName as NSString).appendingPathComponent(plistFileName)
let plist = File(plistPath)
try! plist.open(.write)
try! plist.write(string: "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" +
"<plist version=\"1.0\">\n" +
"<dict>\n" +
"<key>MyString</key>\n" +
"<string>Hello World!</string>\n" +
"<key>MyInteger</key>\n" +
"<integer>100</integer>\n" +
"</dict>\n" +
"</plist>\n"
)
plist.close()
}
See https://github.com/crspybits/SMServerLib for the full context.
Related
I am attempting to write some integration tests against aspnet core 2.2. I want to use a TestStartup class that inherits from the normal Startup class to configure resources and services for testing purposes.
A simple example (can be found here: https://github.com/davidgouge/aspnet-integration-testing):
I have a solution that contains two projects:
IntegrationTestingWeb (a barebones aspnet mvc app)
IntegrationTestingTests (a testing project)
I have a test that uses the Web Startup class and asserts that OK is returned from /Home/Privacy
[Test]
public async Task GetPrivacy_Through_Normal_Startup()
{
var builder = new WebHostBuilder().UseStartup<Startup>();
var client = new TestServer(builder).CreateClient();
var result = await client.GetAsync("/Home/Privacy");
result.StatusCode.Should().Be(HttpStatusCode.OK);
}
This test passes.
If I create a TestStartupInTestProject class that inherites from Startup but place it in the Tests project, then I have to do some extra work when creating the WebHostBuilder but then the test fails.
[Test]
public async Task GetPrivacy_Through_Test_Startup_In_Test_Project()
{
var builder = new WebHostBuilder().ConfigureServices(services =>
{
var startupAssembly = typeof(TestStartupInTestProject).GetTypeInfo().Assembly;
var manager = new ApplicationPartManager();
manager.ApplicationParts.Add(new AssemblyPart(startupAssembly));
manager.ApplicationParts.Add(new AssemblyPart(typeof(HomeController).Assembly));
manager.FeatureProviders.Add(new ControllerFeatureProvider());
manager.FeatureProviders.Add(new ViewComponentFeatureProvider());
services.AddSingleton(manager);
}).UseStartup<TestStartupInTestProject>();
var client = new TestServer(builder).CreateClient();
var result = await client.GetAsync("/Home/Privacy");
result.StatusCode.Should().Be(HttpStatusCode.OK);
}
The error in the failure is:
Tests.Tests.GetPrivacy_Through_Test_Startup_In_Test_Project
System.InvalidOperationException : The view 'Privacy' was not found. The
following locations were searched:
/Views/Home/Privacy.cshtml
/Views/Shared/Privacy.cshtml
/Pages/Shared/Privacy.cshtml
So it looks like because my Startup class is located in the Test project, the views cannot be located. What setting am I missing to be able to find the Views?
It turns out I was missing .UseContentRoot(Directory.GetCurrentDirectory() + "\\..\\..\\..\\..\\IntegrationTestingWeb") when creating the WebHostBuilder. As it sounds, it sets the root dir where the app will look for Views etc.
I'm working on integrating a C++ library (the GRT, a machine learning toolkit, to be specific) inside of an iOS app.
I've built the GRT as a framework, including using some Objective-C++ wrapper functions to call between my app and the framework.
At the moment, I'm trying to troubleshoot something involving file loading. Specifically, I'm trying to load a file from my app bundle into a GRT module.
Here's where I get the file I want access to, and initialize the GRT wrapper:
func loadTrainingData(){
let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileUrl = documentsUrl.appendingPathComponent("train.grt")
let pipeline = GestureRecognitionPipeline()
let test:Bool = pipeline.load(fileUrl.path)
print(test)
}
And here's the Obj-C++ wrapper code that's called when pipeline.load is called:
- (BOOL)load:(NSString *) path
{
BOOL result = self.instance->load(std::string(path.UTF8String));
if (result) {
std::cout << "GRT config";
std::cout << self.instance->getModelAsString();
std::cout << "GRT info: " << self.instance->getInfo();
}
return result;
}
Finally, here's the actual C++ code that's part of the GRT library, where file loading is handled:
bool GestureRecognitionPipeline::load(const std::string &filename){
std::fstream file;
//Clear any previous setup
clear();
file.open(filename.c_str(), std::iostream::in );
if( !file.is_open() ){
errorLog << __GRT_LOG__ << " Failed to open file with filename: " << filename << std::endl;
return false;
}
...
}
Currently, I'm always failing to have my pipeline object successfully import a file. I don't think it's necessarily something to do with the way I'm accessing the file on the iOS side (though, I could be wrong). Does anyone have any insight? Any appreciated - thanks!
EDIT: I was able to verify that I am loading my file is being loaded properly by this check:
let path = Bundle.main.path(forResource: "acc-orientation", ofType: "grt")
print(path as Any!)
But, I'm still getting the same issues as before.
EDIT 2 I verified that the path is being loaded correctly in the the Obj-C++ wrapper too; which leads me to think it may be something related to the way that is handled in iOS....totally lost here now...
EDIT 3 As suggested by a colleague, I tried using the absoluteString of the file url to pass to my wrapper and the underlying C++ code, since the C++ doesn't have access to the sandboxed environment of iOS. Still the same result:
let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileUrl = documentsUrl.appendingPathComponent("acc-orientation.grt")
let pipeline = GestureRecognitionPipeline()
let test:Bool = pipeline.load(fileUrl.absoluteString)
EDIT 4 As suggested in the comments, I tried using fileSystemRepresentation, but this also didn't bring success.
- (BOOL)load:(NSURL *) url {
BOOL result = self.instance->load(std::string([url fileSystemRepresentation]));
...
}
EDIT 5: I made a simple test project that attempts to only access the file and load it using Swift->Obj-C++->C++ (no framework files, in other words). Here's a link where it can be downloaded. Any help appreciated!
Well, you are almost there. I have downloaded your sample project and got it working. Your problem has to do with the actual location of the file you want to open. At the moment you are trying to open the file from the Documents folder but you never actually copy the file from the App Bundle to the Documents folder. So there are two solutions:
Solution 1: App Bundle
Alter the loadTrainingData method in ViewController.swift to access the file from the App Bundle:
func loadTrainingData(){
let path = Bundle.main.url(forResource: "acc-orientation", withExtension: "grt")
let wrapper = Wrapper()
let test:Bool = wrapper.load(path)
print(test)
}
Solution 2: Documents folder
Copy the file from your App Bundle to your Documents folder right after the first launch. Therefore, copy the following code snippet to your AppDelegate.swift:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
do {
let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("acc-orientation.grt")
let bundleURL = Bundle.main.url(forResource: "acc-orientation", withExtension: "grt")
try FileManager.default.copyItem(at: bundleURL!, to: url)
} catch {
print("File already exists")
}
return true
}
With either of these solutions your FileLoader::load method will return true.
Hope that helps.
First of all I know what the problem is, I just don't know Nancy well enough to know how to fix it.
I have a unit test failing when as part of the appharbor build process. The same test also fails when NCrunch executes it. But, when executed by VS2012 it works fine.
The test looks like this:
[Test]
public void Get_Root_Should_Return_Status_OK()
{
// Given
var browser = new Browser(new Bootstrapper());
// When
var result = browser.Get("/");
// Then
Assert.AreEqual(HttpStatusCode.OK, result.StatusCode);
}
HomeModule part handling the "/" route looks like this:
Get["/"] = _ => View["home.sshtml"];
home.sshtml is in the Views folder.
If I replace the above with:
Get["/"] = _ => "Hello World!;
Then the test goes green.
So plainly the problem is that when running the test in NCrunch and appharbor the home.sshtml file cannot be found.
How do I explicitly tell Nancy where the file is?
PS The view file is being copied to the output directory.
PPS I have also tried explicitly telling Nancy where the Views are like and that doesn't work either.
protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
{
var directoryInfo = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory;
if (directoryInfo != null)
Environment.CurrentDirectory = directoryInfo.FullName;
Conventions.ViewLocationConventions.Add((viewName, model, viewLocationContext) => String.Concat("Views/", viewName));
}
The problem is due to the fact that NCrunch doesn't copy the views to the output directory when it compiles and copies the bin directory for running the tests.
What you need to do is set the views to Copy Always, and then in your unit testing project add a IRootPathProvider implementation:
public class StaticPathProvider : IRootPathProvider
{
public static string Path { get; set; }
public string GetRootPath()
{
return Path;
}
}
(Not entirely sure on the path, I can't remember, think it's just where the executing assembly is)
And register that in your bootstrapper for unit tests.
var browserParser = new Browser(with =>
{
...
with.RootPathProvider<StaticPathProvider>();
...
});
Downside is when deploying you need to delete the view directory from your /bin directory.
The alternative is to do what you've already done, embed your views.
I'm just getting my feet wet in Nancy. I was really excited to see the Testing process in the Wiki, but when I tried the following I couldn't get it work pass the tests at first.
Using VS2010
Created Empty ASP.NET Web Application Project: Notify.App
Install-Package Nancy.Hosting.AspNet
Created simple Module as listed below: NotifyModule
Created Class Library Project: Notify.UnitTests
Install-Package Nancy.Testing
Install-Package XUnit
Created simple first test: BaseUrlSpec.cs
Using DefaultNancyBootstrapper the test fails with HttpStatusCode.NotFound.
If I replace the bootstrapper definition with:
var bootstrapper = new ConfigurableBootstrapper(
with =>
with.Module<NotifyModule>());
then the test passes. I don't understand why the SDHP using the DefaultNancyBootstrapper didn't work? Did I do something wrong to make it break, or am I missing details in my understanding?
NotifyModule
using Nancy;
public class NotifyModule : NancyModule {
public NotifyModule() {
Get["/"] = _ => HttpStatusCode.OK;
}
}
BaseUrlSpec
using Nancy;
using Nancy.Testing;
using Notify.App;
using Xunit;
public class BaseUrlSpec
{
[Fact]
public void ShouldRespondOk()
{
var bootstrapper = new DefaultNancyBoostrapper();
var app = new Browser(bootstrapper);
var response = app.Get("/", with => with.HttpRequest());
var statusCode = response.StatusCode;
Assert.Equal(HttpStatusCode.OK, statusCode);
}
}
You need to make sure the assembly containing your route is loaded. Referencing a type from your assembly ensures this, therefore the version using the configurable bootstrapper works.
To make the other one work, just add a reference to some type from your assembly. No need to instantiate it.
I am trying to run the default service unit test in my project (Taken from the Angular Seed project on GitHub), but I keep getting the error "module is not defined".
I have read that it could be something to do with the order of the referenced JavaScript files, but I can't seem to get it to work, so hopefully one of you might be able to help.
My configuration for the test looks like this:
basePath = '../';
files = [
'public/javascripts/lib/jquery-1.8.2.js',
'public/javascripts/lib/angular.js',
'public/javascripts/lib/angular-.js',
'public/app.js',
'public/controllers/.js',
'public/directives.js',
'public/filters.js',
'public/services.js',
JASMINE,
JASMINE_ADAPTER,
'public/javascripts/lib/angular-mocks.js',
'test/unit/*.js' ];
autoWatch = true;
browsers = ['Chrome'];
junitReporter = { outputFile: 'test_out/unit.xml', suite: 'unit'
};
The service looks like the following:
angular.module('myApp.services', []).
value('version', '0.1');
The test looks like this:
'use strict';
describe('service', function() {
beforeEach(module('myApp.services'));
describe('version', function() {
it('should return current version', inject(function(version) {
expect(version).toEqual('0.1');
}));
});
});
And the error when running the test through testacular is this:
ReferenceError: module is not defined
You are missing the angular-mocks.js file.
I had the same problem, and I understood why it wasn't working:
The jasmine.js javascript must be referenced BEFORE the angular-mocks.js file.
Indeed, the angular-mocks.js checks if Jasmine is loaded, and only if it is it will add the module function to the window.
Here is an extract of Angular Mocks code:
(Edit after the few comments about 'hacking' I had below: this is just an extract of the code, this is not something you need to write yourself, it's already there!)
window.jasmine && (function(window) {
[...]
window.module = angular.mock.module = function() {
var moduleFns = Array.prototype.slice.call(arguments, 0);
return isSpecRunning() ? workFn() : workFn;
/////////////////////
[...]
};
In a nutshell:
Just reference your jasmine.js before angular-mocks.js and off you go.
The window.module function comes in angular-mocks.js and is a shorthand for angular.mock.module. As mentioned in the docs, the module function only works with Jasmine.
Using Testacular, the following example configuration file will load angular-mocks.js.
/** example testacular.conf.js */
basePath = '../';
files = [
JASMINE,
JASMINE_ADAPTER,
'path/to/angular.js',
'path/to/angular-mocks.js', // for angular.mock.module and inject.
'src/js/**/*.js', // application sources
'test/unit/**/*.spec.js' // specs
];
autoWatch = true;
browsers = ['Chrome'];
And, as suggested elsewhere, you can run Testacular with debug logging to see what scripts are loaded (you can also see the same in the inspector):
testacular --log-level debug start config/testacular.conf.js
The angular.mock.inject docs include a pretty complete example.
We use 'module' without 'angular' in our unit tests and it works fine.
CoffeeScript:
describe 'DiscussionServicesSpec', ->
beforeEach module 'DiscussionServices'
beforeEach inject ... etc.
which compiles to
JavaScript:
describe('DiscussionServices', function() {
beforeEach(module('DiscussionServices'));
beforeEach(inject(function ... etc.
The only time I see something like the error you described is if in the testacular.conf.js file the angular-mocks.js file is not listed in the files section before the specs trying to use 'module'. If I put it after my tests in the 'files' list I get
ReferenceError: Can't find variable: module
(Our tests are being run through PhantomJS)
I had included angular-mocks.js in my karma config, but was still getting the error. It turns out the order is important in the files array. (duh) Just like in the head of an html doc, if a script calls angular before it's defined, and error occurs. So I just had to include my app.js after angular.js and angular-mocks.js.
If you're using Yeoman and its angular-generator, you probably get this error. Especially when you do the Tutorial ( ._.)
I fixed it, by copying the angular-mocks.js file, from the bower_components/angular-mocks dir to the test/mock dir. Of course you have to be sure, that your karma.conf.js file is configured correctly.
Greetings!
I had this same issue when I was doing something like var module = angular.module('my',[]). I needed to make sure it was surrounded by IIFE