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.
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 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.
I am using Rhino.Mocks and Structure map to help unit test my code. I have several tests that pass when they are ran by themselves, but when ran as a group fail to pass. The setup code for these unit tests is:
[TestInitialize()]
public void Setup()
{
ObjectFactory.Initialize(x =>
{
x.For(IManager)().Use(Handler)();
});
}
In my tests, I stub out this interface and call the method.
[TestMethod]
public void AreMultiple_Test()
{
var mackIManager = MockRepository.GenerateMock<IManager>();
mackIManager.Stub(u => u.GetTwoUserName(Arg<int>.Is.Anything)).Return(null);
ObjectFactory.Inject(typeof(IManager), mackIManager);
StepAdditionalActionBase actionBase = new StepAdditionalActionBase();
bool areMultiple = actionBase.AreMultiple(new WorkOrder { Id = "123" });
Assert.IsFalse(areMultiple);
}
Test Method 2
[TestMethod]
public void AreMultiple_Test()
{
var mackIManager = MockRepository.GenerateMock<IManager>();
mackIManager.Stub(u => u.GetTwoUserName(Arg<int>.Is.Anything)).Return("123");
ObjectFactory.Inject(typeof(IManager), mackIManager);
StepAdditionalActionBase actionBase = new StepAdditionalActionBase();
bool areMultiple = actionBase.AreMultiple(new WorkOrder { Id = "123" });
Assert.IsTrue(areMultiple);
}
This is unit testing the following code.
public bool AreMultiple(WorkOrder workOrder)
{
string secondUser = _handler.GetTwoUserName(_workflowManager.GetNumberForProject(workOrder.Id));
if (String.IsNullOrEmpty(secondUser ))
{
return false;
}
return true;
}
When I run them by themselves, they work fine. When I run them together, the first passes and the second fails. When I debug the second one, I find that that the return value in the Stubbed method is still coming back as null. How do I get this to use the new Stubbed method.
UPDATE.
I am using StructureMap as my container. From what I have been able to find, the following code is what is used to dispose of the container I got it from this link. When I added this, the test still fail when ran together, but pass when ran individually.
[TestCleanup()]
public void TestCLeanup()
{
ObjectFactory.Container.Dispose();
}
The tests work one by one but fails if run all together. The problem should be in the common part which is being shared across the tests making them dependent from each other. In this particular case that is static ObjectFactory which is nothing else but a Service Locator (anti-pattern).
In the tests, you mock the IManager interface and register it in the ObjectFactory:
ObjectFactory.Inject(typeof(IManager), mackIManager);
Then the SUT uses the ObjectFactory service locator to resolve and use the mocked interface (_handler field):
string secondUser = _handler.GetTwoUserName(...)
I suspect the first test registers the _handler and never clean it up properly, so that the same instance appears in the second test. You should reset the ObjectFactory between tests following the Register Resolve Release pattern.
Another (preferable) option is to refactor your SUT to receive the IManager handler dependency explicitly via constructor. That would simplify both SUT and tests moving the ObjectFactory configuration to the Composition Root.
I'm doing some tests with PHPUnit and Selenium and i would like all of them to run in the same browser window.
I've tried starting the Selenium Server with
java -jar c:\php\selenium-server-standalone-2.33.0.jar -browserSessionReuse
but with no visible change.
I've also tried with shareSession() in the setup
public function setUp()
{
$this->setHost('localhost');
$this->setPort(4444);
$this->setBrowser('firefox');
$this->shareSession(true);
$this->setBrowserUrl('http://localhost/project');
}
but the only change is that it opens a window for every test, and not really sharing the session. I'm out of ideas at this point.
My tests look like this:
public function testHasLoginForm()
{
$this->url('');
$email = $this->byName('email');
$password = $this->byName('password');
$this->assertEquals('', $email->value());
$this->assertEquals('', $password->value());
}
Here's the elegant solution. To share browser sessions in Selenium2TestCase, you must set sessionStrategy => 'shared' in your initial browser setup:
public static $browsers = array(
array(
'...
'browserName' => 'iexplorer',
'sessionStrategy' => 'shared',
...
)
);
The alternative (default) is 'isolated'.
You do not need to use the flag -browserSessionReuse
In your case The set up function running before every test and starting new instance.
This is what i did to prevent this to happen (Its little bit ugly but work for me both in Windows and Ubuntu):
I created helper class with static ver: $first and initialized it.
helper.php:
<?php
class helper
{
public static $first;
}
helper::$first = 0;
?>
Edit main test file setUp() function(and add require_once to helper.php):
require_once "helper.php";
class mySeleniumTest extends PHPUnit_Extensions_SeleniumTestCase
{
public function setUp()
{
$this->setHost('localhost');
$this->setPort(4444);
if (helper::$first == 0 )
{
$this->shareSession(TRUE);
$this->setBrowser('firefox');
$this->setBrowserUrl('http://localhost/project');
helper::$first = 1 ;
}
}
....
setHost and setPort outside the if because the values restarted after each test(For me...) and need to set up every time (if the selenium server is not localhost:4444)
Just found an (much) faster way to proceed : If you perform several test in one function, all test are performed in the same window. The setback is that the tests and reporting won't be nicely presented by tests, but the speed is way up!
In the same function for each test just use:
$this->url('...');
Or
$this->back();
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.