Page 28 - MSDN Magazine, September 2019
P. 28
dotnet new xunit -lang F# -o tests/LibTests
dotnet add tests/LibTests/LibTests.fsproj reference src/Library/Library.fsproj dotnet sln add tests/LibTests/LibTests.fsproj
Now I’ll replace the Tests.fs contents in the test project with the following:
module Tests open Xunit
[<Fact>]
let ``Test Hello`` () =
let expected = """I used to be Hello but now I'm "Hello" thanks to JSON.NET!""" let actual = Library.getJsonNetJson "Hello"
Assert.Equal(expected, actual)
This simple test just verifies that the output is correct. Note that the test name is enclosed by double-backticks to allow the use of a more natural name for the test. This is quite common in F# testing. Additionally, you use the triple-quoted string when you want to embed quoted strings in F#. Alternatively, you could use backslashes if you prefer.
Now I can run the test:
dotnet test
And the output verifies that it passes!
Starting test execution, please wait...
Test Run Successful. Total tests: 1
Passed: 1
Total time: 4.9443 Seconds
Ta-da! With a minimal toolset, it’s entirely possible to build a library that’s unit-tested and a console app that runs that library code. This alone is enough to actually build a full solution, espe- cially if you pair it with an F# code editor like Visual Studio Code and the Ionide plug-in. In fact, many professional F# developers use only this to do their daily work! You can find the full solution on GitHub at bit.ly/2Svquuc.
This is quite fun, but let’s look at some material that’s more interesting than a library project or a console app.
Build a Web App with F#
F# can be used for a lot more than just library projects and console apps. Among the most common solutions F# developers build are Web apps and services. There are three main options for doing this, and I’ll briefly discuss each one:
Giraffe (bit.ly/2Z4zPeP) is best thought of as “functional program- ming bindings to ASP.NET Core.” It’s fundamentally a middleware library that exposes routes as F# functions. Giraffe is somewhat of a “bare bones” library that’s relatively unopinionated about how you build your Web services or Web app. It offers some wonderful ways to compose routes using functional techniques and has some nice built-in functions to simplify things like working with JSON or XML, but how you compose your projects is entirely up to you. Lots of F# developers use Giraffe because of how flexible it is, and because it has such a rock-solid foundation as it uses ASP.NET Core under the covers.
Saturn (bit.ly/2YjgGsl) is sort of like “functional programming bindings to ASP.NET Core, but with batteries included.” It uses a form of the MVC pattern, but done functionally rather than with object-oriented programming abstractions. It’s built atop Giraffe and shares its core abstractions, but it gives a much more opin- ionated take on how to build Web apps with .NET Core. It also
20 msdn magazine
includes some .NET CLI command-line tools that offer database model generation, database migrations, and scaffolding of Web controllers and views. Saturn includes more built-in functions than Giraffe as a result of being more opinionated, but it does require that you buy into the approach it enforces.
Suave (bit.ly/2YmDRSJ) has been around for a lot longer than Giraffe and Saturn. It was the primary influence for Giraffe because it pioneered the programming model that both use in F# for Web services. A key difference is that Suave has its own OWIN-com- pliant Web server that it uses, rather than sitting atop ASP.NET Core. This Web server is highly portable, as it can be embedded into low-powered devices via Mono. The programming model for Suave is slightly simpler than Giraffe due to not needing to work with ASP.NET Core abstractions.
With a minimal toolset, it’s entirely possible to build a library that’s unit-tested and a console app that runs that library code.
I’ll start by building a simple Web service with Giraffe. It’s easy with the .NET CLI:
dotnet new -i "giraffe-template::*"
dotnet new giraffe -lang F# -V none -o GiraffeApp cd GiraffeApp
Now I can build the application by running either build.bat or sh build.sh.
Now I’ll run the project:
dotnet run -p src/GiraffeApp
I can then navigate to the route given by the template /api/hello, via localhost.
Navigating to https://localhost:5001/api/hello gives me:
{"text":"Hello world, from Giraffe!"}
Cool! Let’s take a look at how this was generated. To do that, I’ll open up the Program.fs file and note the webApp function:
let webApp = choose [
subRoute "/api" (choose [
GET >=> choose [
route "/hello" >=> handleGetHello
] ])
setStatusCode 404 >=> text "Not Found" ]
There’s a bit going on here, and it’s actually backed by some rather intricate functional programming concepts. But it’s ultimately a domain-specific language (DSL), and you don’t need to under- stand every little bit of how it works to use it. Here are the basics:
The webApp function is comprised of a function called choose. The choose function is executed by the ASP.NET Core runtime whenever someone makes a request to the server. It will look at the request and try to find a route that matches. If it can’t, it will fall back to the 404 route defined at the bottom.
Because I have a subRoute defined, the choose function will know to crawl all of its child routes. The definition of what to crawl F#