Why doesn't `DefaultNancyBoostrapper` find my NancyModule - unit-testing

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.

Related

How to unit test a single conversational statement

i am working with bots and the Microsoft Bot Framework.
I used the DispatchBot template to generate my bot. (https://learn.microsoft.com/de-de/azure/bot-service/bot-builder-tutorial-dispatch?view=azure-bot-service-4.0&tabs=cs)
For conversational testing, i want to create unit tests. Therefore i used this documentation to gather some informations (https://learn.microsoft.com/de-de/azure/bot-service/unit-test-bots?view=azure-bot-service-4.0&tabs=csharp)
The thing is that i dont want to test dialogs, but a single statement (a question and the right answer)
How can i implement this?
Here you can see the Start of my Dispatchbot.cs file where the magic happens (search of the correct Knowledge Base etc.)
Here's a link to how we create tests for CoreBot. The part you're most likely interested in is testing things under the /Bots directory. Based off of the test code you can find there, you likely want something like:
using System;
using System.Threading;
using System.Threading.Tasks;
using CoreBot.Tests.Common;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Adapters;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.BotBuilderSamples.Bots;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;
namespace KJCBOT_Tests
{
public class BotTests
{
[Fact]
public async Task TestResponseToQuesion()
{
// Note: this test requires that SaveChangesAsync is made virtual in order to be able to create a mock.
var memoryStorage = new MemoryStorage();
var mockConversationState = new Mock<ConversationState>(memoryStorage)
{
CallBase = true,
};
var mockUserState = new Mock<UserState>(memoryStorage)
{
CallBase = true,
};
// You need to mock a dialog because most bots require a Dialog to instantiate it.
// If yours doesn't you can likely skip this
var mockRootDialog = SimpleMockFactory.CreateMockDialog<Dialog>(null, "mockRootDialog");
var mockLogger = new Mock<ILogger<DispatchBot<Dialog>>>();
// Act
var sut = new DispatchBot<Dialog>(mockConversationState.Object, mockUserState.Object, mockRootDialog.Object, mockLogger.Object);
var testAdapter = new TestAdapter();
var testFlow = new TestFlow(testAdapter, sut);
await testFlow
.Send("<Whatever you want to send>")
.AssertReply("<Whatever you expect the reply to be")
.StartTestAsync();
}
}
}

ASP.net core integration testing with inherited Startup class. Unable to locate View

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.

Unit Testing ODataQueryOptions Gives MissingMethodException DependencyInjection

So here is my problem, I have an OData Web Api service that uses ODataQueryOptions to filter data from our sql server and I am trying to setup a .Net Framework Unit Test project to test the controllers with different query options. I have been searching for several days now and found many examples but most of them use an older version of OData. This example is the best one I have found so far, the only problem is that calling config.EnableDependencyInjection(); gives me the following exception:
Method not found: 'System.IServiceProvider Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(Microsoft.Extensions.DependencyInjection.IServiceCollection)'.
Here is an example of my code:
using System.Collections.Generic;
using System.Web.Http.Results;
using System.Web.OData;
using System.Web.OData.Query;
using System.Net.Http;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using University.API.OData.Controllers;
using University.API.OData.Models;
using System.Web.OData.Routing;
using System.Web.Http;
using System.Web.OData.Extensions;
[TestClass]
public class SalesforceUnitTest
{
private HttpRequestMessage request;
private ODataQueryOptions<Product> _options;
[TestInitialize]
public void TestInitialize()
{
request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/odata/product?$top=5");
var model = WebApiConfig.GetModel();
HttpConfiguration config = new HttpConfiguration();
config.EnableDependencyInjection(); //Throws Missing Method Exception
WebApiConfig.Register(config);
config.EnsureInitialized();
request.SetConfiguration(config);
ODataQueryContext context = new ODataQueryContext(
model,
typeof(Product),
new ODataPath(
new Microsoft.OData.UriParser.EntitySetSegment(
model.EntityContainer.FindEntitySet("product"))
)
);
_options = new ODataQueryOptions<Product>(context, request);
}
[TestMethod]
public void ProductTest()
{
var controller = new ProductController();
controller.Request = request;
var response = controller.Get(_options);
var contentResult = response as OkNegotiatedContentResult<List<Product>>;
Assert.IsNotNull(contentResult);
Assert.IsNotNull(contentResult.Content);
}
}
Let me know if there is any other information you may need.
Thank you for any help you can provide.
EDIT:
Here what is referenced in the unit test project:
EntityFramework
EntityFramework.SqlServer
Microsoft.Data.Edm
Microsoft.Data.OData
Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.DependencyInjection.Abstractions
Microsoft.OData.Core
Microsoft.Odata.Edb
Microsoft.Spatial
Microsoft.Threading.Tasks
Microsoft.Threading.Tasks.Extensions
Microsoft.Threading.Tasks.Extensions.Desktop
Microsoft.VisualStudio.TestPlatform.TestFramework
Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions
System
System.ComponentModel.DataAnnotations
System.Net
System.Net.Http
System.Net.Http.Extensions
System.Net.Http.Primitives
System.Net.Http.WebRequest.
System.Spatial
System.Web
System.Web.Http
System.Web.OData
ODataAPI
I figured it out after some more digging. It seems that my Unit Test project was using a different version than my ODataApi project. This for some weird reason was causing the MissingMethodException instead of a VersionMismatchException. Hopefully this helps someone else who is looking into why Dependency Injection isnt working for your Unit Test project.

Moq out parameters

I'm fairly new to using Moq and Nunit for unit testing and I'm having issues with one scenario. What I want is for my mock to have an out parameters which my system under test will then use to decide what action to take.
My system under test is an MVC API controller and in particular I'm trying to test the POST method. I want to return an error message for the object when validation fails.
Here is the method code for the controller:
public IHttpActionResult Post(Candidate candidate)
{
try
{
if(candidate==null)
return BadRequest();
IEnumerable<string> errors;
_candidateManager.InsertCandidate(candidate, out errors);
if (errors!=null && errors.Any())
return BadRequest(CreateErrorMessage("Invalid candidate: ", errors));
return CreatedAtRoute("DefaultApi", new {id = candidate.CandidateId}, candidate);
}
catch (Exception)
{
return InternalServerError();
}
}
This is my Unit Test Code:
[Test]
[Category("CandidateManagerController Unit Tests")]
public void Should_Return_Bad_Request_When_Creating_Invalid_Candidate()
{
IEnumerable<string> errors = new List<string>() {"error1", "error2"};
var mockManager = new Mock<ICandidateManager>();
mockManager.Setup(x => x.InsertCandidate(new Candidate(), out errors)).Callback(()=>GetErrors(errors));
var sut = new CandidateManagerController(mockManager.Object);
var actionResult = sut.Post(new Candidate());
Assert.IsInstanceOf<BadRequestResult>(actionResult);
}
What I expect is that when _candidateManager.InsertCandidate() is run then the errors variable is populated. However what is happening is that when you step through the controller code errors is null after _candidateManager.InsertCandidate() method is run.
If anyone has any ideas what I'm doing wrong or if what I want to do is not possible using Moq then please let me know.
Thanks
What you want to do is possible. If you look at the Quickstart docs at https://github.com/Moq/moq4/wiki/Quickstart, there is a section where it shows how you do setups for methods using out params. I have made two corrections to your code and it works.
You have to use the same candidate instance for both the mock setup and when you exercise the sut. Otherwise, Moq thinks that the two objects are different and your test setup becomes useless.
You don't have to use Callback in order to set the errors returned by the mocked CandidateManager.
Below is your test method with my changes.
[Test]
[Category("CandidateManagerController Unit Tests")]
public void Should_Return_Bad_Request_When_Creating_Invalid_Candidate()
{
IEnumerable<string> errors = new List<string>() {"error1", "error2"};
//instance to be used for both setup and test later
var candidate = new Candidate();
var mockManager = new Mock<ICandidateManager>();
//removed Callback
mockManager.Setup(x => x.InsertCandidate(candidate, out errors));
var sut = new CandidateManagerController(mockManager.Object);
var actionResult = sut.Post(candidate);
Assert.IsInstanceOf<BadRequestResult>(actionResult);
}
You have to make sure that when you call your SUT that you use the same instance passed to the out argument otherwise the call will fail.
In your example, the method under test passes a null instance into the mocked method thus negating the setup of the test.
If however you are not able to supply the same instances for the out then it doesn't look like you will be able to get a mock to pass successfully. Take a look a the Quick Start for Moq to get an understanding of it capabilities.

How do I explicitly specify where a Nancy view is located?

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.