Introduction

In my previous post I made a Plantsmodule which had 2 routes. One of those routes had a problem when tried to put a string in the parameter and that was less than optimal. It was time for tests to make it easier on myself to test these kinds of things.

The module

So here is my module.

vb.net
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Imports WebApplication2.Model
Imports Nancy
 
Public Class PlantsModule
    Inherits NancyModule
 
    Public Sub New()
        MyBase.Get("/plants") = Function(parameters)
                                    Return View(New PlantsModel() With {.Plants = New List(Of PlantModel) From {New PlantModel() With {.Id = 2, .Name = "test"}}})
 
                                End Function
        MyBase.Get("/plants/{Id}") = Function(parameters)
                                         If parameters.id = 1 Then
                                             Return View(New PlantModel() With {.Id = 1, .Name = "test"})
                                         Else
                                             Return View(New PlantModel() With {.Id = 2, .Name = "test"})
                                         End If
                                     End Function
 
    End Sub
 
End Class
Imports WebApplication2.Model
Imports Nancy

Public Class PlantsModule
    Inherits NancyModule

    Public Sub New()
        MyBase.Get("/plants") = Function(parameters)
                                    Return View(New PlantsModel() With {.Plants = New List(Of PlantModel) From {New PlantModel() With {.Id = 2, .Name = "test"}}})

                                End Function
        MyBase.Get("/plants/{Id}") = Function(parameters)
                                         If parameters.id = 1 Then
                                             Return View(New PlantModel() With {.Id = 1, .Name = "test"})
                                         Else
                                             Return View(New PlantModel() With {.Id = 2, .Name = "test"})
                                         End If
                                     End Function

    End Sub

End Class

This one throws an exception when you enter abc or any other bad value.

The tests

To begin with you should nuget the Nancy.Testing package.

Then you can use the Browser.

For our purpose it would look something like this.

vb.net
1
2
3
4
5
6
7
8
9
10
11
Private _browser As Browser
 
    <SetUp()>
    Public Sub FixtureSetup()
        Dim configuration = A.Fake(Of IRazorConfiguration)()
        Dim bootstrapper = New ConfigurableBootstrapper(Sub(config)
                                                            config.Module(Of PlantsModule)()
                                                            config.ViewEngine(New RazorViewEngine(configuration))
                                                        End Sub)
        _browser = New Browser(bootstrapper)
    End Sub
Private _browser As Browser

    <SetUp()>
    Public Sub FixtureSetup()
        Dim configuration = A.Fake(Of IRazorConfiguration)()
        Dim bootstrapper = New ConfigurableBootstrapper(Sub(config)
                                                            config.Module(Of PlantsModule)()
                                                            config.ViewEngine(New RazorViewEngine(configuration))
                                                        End Sub)
        _browser = New Browser(bootstrapper)
    End Sub

So we tell it what Module to use and we tell it what Engine to use. We also use FakeItEasy to make a mock for our RazorConfiguration. For the next part I got some help from this blogpost by Jef Claes.

if you leave out the Engine you will get this error.

System.Exception : ConfigurableBootstrapper Exception
—-> Nancy.RequestExecutionException : Oh noes!
—-> Nancy.ViewEngines.ViewNotFoundException : Unable to locate view ‘Plants’
Currently available view engine extensions: sshtml,html,htm
Locations inspected: ,,,,views/Plants/Plants,Plants/Plants,views/Plants,Plants
Root path: E:UserschristiaanAppDataLocalNCrunch708461WebApplication2bin

Do you notice how it does not mention cshtml or vbhtml in view engine extensions? That is solved by telling it what engine to use.

In theory that should work but you will get this error when you try it.

System.Exception : ConfigurableBootstrapper Exception
—-> Nancy.RequestExecutionException : Oh noes!
—-> Nancy.ViewEngines.ViewNotFoundException : Unable to locate view ‘Plants’
Currently available view engine extensions: sshtml,html,htm,cshtml,vbhtml
Locations inspected: ,,,,views/Plants/Plants,Plants/Plants,views/Plants,Plants
Root path: E:UserschristiaanAppDataLocalNCrunch708461WebApplication2bin

That is solved by the RootPathProvider we find in the blogpost by Jef Claes. In VB.Net that would look like this.

vb.net
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
Imports System.IO
Imports Nancy
 
Public Class RootPathProvider
    Implements IRootPathProvider
 
    Private Shared _cachedRootPath As String
 
    Public Function GetRootPath() As String Implements IRootPathProvider.GetRootPath
        If Not String.IsNullOrEmpty(_cachedRootPath) Then Return _cachedRootPath
        Dim currentDirectory = New DirectoryInfo(Environment.CurrentDirectory)
        Dim rootPathFound = False
        While Not rootPathFound
            Dim directoriesContainingViewFolder = currentDirectory.GetDirectories("Views", SearchOption.AllDirectories)
            If directoriesContainingViewFolder.Any() Then
                _cachedRootPath = directoriesContainingViewFolder.First().FullName
                rootPathFound = True
            End If
            currentDirectory = currentDirectory.Parent
        End While
        Return _cachedRootPath
    End Function
 
    Public Function Equals1(obj As Object) As Boolean Implements IHideObjectMembers.Equals
 
    End Function
 
    Public Function GetHashCode1() As Integer Implements IHideObjectMembers.GetHashCode
 
    End Function
 
    Public Function GetType1() As Type Implements IHideObjectMembers.GetType
 
    End Function
 
    Public Function ToString1() As String Implements IHideObjectMembers.ToString
 
    End Function
End Class
Imports System.IO
Imports Nancy

Public Class RootPathProvider
    Implements IRootPathProvider

    Private Shared _cachedRootPath As String

    Public Function GetRootPath() As String Implements IRootPathProvider.GetRootPath
        If Not String.IsNullOrEmpty(_cachedRootPath) Then Return _cachedRootPath
        Dim currentDirectory = New DirectoryInfo(Environment.CurrentDirectory)
        Dim rootPathFound = False
        While Not rootPathFound
            Dim directoriesContainingViewFolder = currentDirectory.GetDirectories("Views", SearchOption.AllDirectories)
            If directoriesContainingViewFolder.Any() Then
                _cachedRootPath = directoriesContainingViewFolder.First().FullName
                rootPathFound = True
            End If
            currentDirectory = currentDirectory.Parent
        End While
        Return _cachedRootPath
    End Function

    Public Function Equals1(obj As Object) As Boolean Implements IHideObjectMembers.Equals

    End Function

    Public Function GetHashCode1() As Integer Implements IHideObjectMembers.GetHashCode

    End Function

    Public Function GetType1() As Type Implements IHideObjectMembers.GetType

    End Function

    Public Function ToString1() As String Implements IHideObjectMembers.ToString

    End Function
End Class

The above presumes your views are in a Views subfolder. Which they are of course.
And now we can start writing tests.

vb.net
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<Test>
    Public Sub IfPlantsRouteReturnsStatusCodeOk()
        Dim result = _browser.Get("/plants", Sub(x)
                                                 x.HttpRequest()
                                             End Sub)
        Assert.AreEqual(HttpStatusCode.OK, result.StatusCode)
    End Sub
 
    <Test>
    Public Sub IfPlantWithId1RouteReturnsStatusCodeOk()
        Dim result = _browser.Get("/plants/1", Sub(x)
                                                   x.HttpRequest()
                                               End Sub)
        Assert.AreEqual(HttpStatusCode.OK, result.StatusCode)
    End Sub
 
    <Test>
    Public Sub IfPlantWithIdabcRouteReturnsStatusCodeOk()
        Dim result = _browser.Get("/plants/abc", Sub(x)
                                                     x.HttpRequest()
                                                 End Sub)
        Assert.AreEqual(HttpStatusCode.NotFound, result.StatusCode)
    End Sub
<Test>
    Public Sub IfPlantsRouteReturnsStatusCodeOk()
        Dim result = _browser.Get("/plants", Sub(x)
                                                 x.HttpRequest()
                                             End Sub)
        Assert.AreEqual(HttpStatusCode.OK, result.StatusCode)
    End Sub

    <Test>
    Public Sub IfPlantWithId1RouteReturnsStatusCodeOk()
        Dim result = _browser.Get("/plants/1", Sub(x)
                                                   x.HttpRequest()
                                               End Sub)
        Assert.AreEqual(HttpStatusCode.OK, result.StatusCode)
    End Sub

    <Test>
    Public Sub IfPlantWithIdabcRouteReturnsStatusCodeOk()
        Dim result = _browser.Get("/plants/abc", Sub(x)
                                                     x.HttpRequest()
                                                 End Sub)
        Assert.AreEqual(HttpStatusCode.NotFound, result.StatusCode)
    End Sub

I just want to check if I get an ok code when it works and a not found when I do plants/abc

But the last test fails. With this exception.

vb.net
1
2
3
4
System.Exception : ConfigurableBootstrapper Exception
  ----> Nancy.RequestExecutionException : Oh noes!
  ----> System.InvalidCastException : Conversion from string "abc" to type 'Double' is not valid.
  ----> System.FormatException : Input string was not in a correct format.
System.Exception : ConfigurableBootstrapper Exception
  ----> Nancy.RequestExecutionException : Oh noes!
  ----> System.InvalidCastException : Conversion from string "abc" to type 'Double' is not valid.
  ----> System.FormatException : Input string was not in a correct format.

Time to change our Module. And do the simplest thing to make our test pass.

vb.net
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Imports WebApplication2.Model
Imports Nancy
 
Public Class PlantsModule
    Inherits NancyModule
 
    Public Sub New()
        MyBase.Get("/plants") = Function(parameters)
                                    Return View(New PlantsModel() With {.Plants = New List(Of PlantModel) From {New PlantModel() With {.Id = 2, .Name = "test"}}})
 
                                End Function
        MyBase.Get("/plants/{Id}") = Function(parameters)
                                         If Not IsNumeric(parameters.id) Then Return HttpStatusCode.NotFound
                                         If parameters.id = 1 Then
                                             Return View(New PlantModel() With {.Id = 1, .Name = "test"})
                                         Else
                                             Return View(New PlantModel() With {.Id = 2, .Name = "test"})
                                         End If
                                     End Function
 
    End Sub
 
End Class
Imports WebApplication2.Model
Imports Nancy

Public Class PlantsModule
    Inherits NancyModule

    Public Sub New()
        MyBase.Get("/plants") = Function(parameters)
                                    Return View(New PlantsModel() With {.Plants = New List(Of PlantModel) From {New PlantModel() With {.Id = 2, .Name = "test"}}})

                                End Function
        MyBase.Get("/plants/{Id}") = Function(parameters)
                                         If Not IsNumeric(parameters.id) Then Return HttpStatusCode.NotFound
                                         If parameters.id = 1 Then
                                             Return View(New PlantModel() With {.Id = 1, .Name = "test"})
                                         Else
                                             Return View(New PlantModel() With {.Id = 2, .Name = "test"})
                                         End If
                                     End Function

    End Sub

End Class

And now all three of our tests pass.

Don’t worry I can write more tests.

Conclusion

I figured it out, so it isn’t that hard ;-).

Update

Since version 0.15 the viewengine also needs a textresource. So now the setup looks like this.

vb.net
1
2
3
4
5
6
7
8
9
10
11
<SetUp()>
        Public Sub FixtureSetup()
            Dim configuration = A.Fake(Of IRazorConfiguration)()
            Dim textResource = A.Fake(Of ITextResource)()
            Dim bootstrapper = New ConfigurableBootstrapper(Sub(config)
                                                                config.Module(Of PlantsModule)()
                                                                config.Dependency(Of ITextResource)(textResource)
                                                                config.ViewEngine(New RazorViewEngine(configuration, textResource))
                                                            End Sub)
            _browser = New Browser(bootstrapper)
        End Sub
<SetUp()>
        Public Sub FixtureSetup()
            Dim configuration = A.Fake(Of IRazorConfiguration)()
            Dim textResource = A.Fake(Of ITextResource)()
            Dim bootstrapper = New ConfigurableBootstrapper(Sub(config)
                                                                config.Module(Of PlantsModule)()
                                                                config.Dependency(Of ITextResource)(textResource)
                                                                config.ViewEngine(New RazorViewEngine(configuration, textResource))
                                                            End Sub)
            _browser = New Browser(bootstrapper)
        End Sub