# Testing solar calculator in Elm?

I have written a solar calculator using info provided by NOAA. There are spreadsheets (both Excel and LibreOffice) containing all the necessary functions needed in the calculations. The functions deliver mostly float-type results.
I read recently about TDD first time and was interested to try it, as a total newbie (in Elm and elm-test)
I wonder now, is there any reasonable way to do some fuzz elm-tests on the float-type functions?
It’s lots easier to write some unit tests comparing the calculated values to expected ones using Expect.atMost with the given tolerances.
The very first unit test, I wrote for Julian Day Number JDN (an Int function for the given date) and Julian Date JD (a Float function for the given date and time) as described in the Wikipedia article.

Any comments and suggestions are welcome.

Look at my github files:
Main.elm
Jdn3Test.elm
GregorJDN.elm

5 Likes

As the next step, I have made kind of unit tests on the calculator functions with elm repl, taking the expect values of the results from NOAA Solar Calculator.
Below is a short example. First are the required functions imported from the file Main.elm:
`> import Main exposing (Model, getCent, getNoon, sunRise, sunSet, getDayLength, mnToHrMn, sunDeclination, solAzimuth)`
Next is the input data set into model structure for the city center Helsinki:
```> helsinki = Model “2022” “9” “4” “10” “19” “29” “60.17” “24.94” “3” { day = “4”, hour = “10”, latitude = “60.17”, longitude = “24.94”, minute = “19”, month = “9”, second = “29”, timezone = “3”, year = “2022” } : Model```
Calculations are performed for the expected noon time according to the NOAA calculator, 10:19:29 UTC which should result the solar azimuth 180.0°.
```> helsinki.latitude “60.17” : String ``````> mnToHrMn <| 60*(getDayLength helsinki) – expect 13:55 “13:54:21” : String```
```> mnToHrMn <| getNoon helsinki – expect 13:19:29 “13:19:18” : String```
```> mnToHrMn <| sunRise helsinki – expect 06:21 “06:22:08” : String```
```> mnToHrMn <| sunSet helsinki – expect 20:16 “20:16:29” : String```
```> 90 - Main.solZenith helsinki – Altitude without refraction corr. 36.93729566691127 : Float```
```> Main.refractCorrectAltitude helsinki – expect 36.96° 36.958715879793985 : Float```
```> solAzimuth helsinki – expect 180.05° 180.0521205431728 : Float```
```> sunDeclination helsinki – expect 7.11° 7.107305165343985 : Float ```
Generally, it can be concluded, the elm version is about as accurate as NOAA calculator. However, NOAA gives the the noon time sligthly deviating from the right value for azimuth 180 degrees and my calculator repeats that for the same given time of course.

I have made the next example during the same session for the German City Hamburg.
The accuracy is here also pretty good. This time, I have adjusted the time of NOAA so that it results as near as possible the right azimuth 180 and now, both calculators give exactly the same values.

```> hamburg = Model “2022” “9” “4” “11” “19” “9” “53.57” “9.98” “2” { day = “4”, hour = “11”, latitude = “53.57”, longitude = “9.98”, minute = “19”, month = “9”, second = “9”, timezone = “2”, year = “2022” } : Model```

``` hamburg.latitude “53.57” : String mnToHrMn <| 60*(getDayLength hamburg) – expect 13:29 “13:29:08” : String mnToHrMn <| getNoon hamburg – expect 13:19:18 “13:19:08” : String mnToHrMn <| sunRise hamburg – expect 06:34 “06:34:34” : String mnToHrMn <| sunSet hamburg – expect 20:03 “20:03:42” : String 90 - Main.solZenith hamburg – Altitude without refraction correction 43.522008630661276 : Float Main.refractCorrectAltitude hamburg – Altitude with refr. corr., expect 43.54° 43.538979722952796 : Float solAzimuth hamburg – expect 180.00° 180.00283296569495 : Float sunDeclination hamburg – expect 7.09° 7.0920086610518585 : Float ```

As the next thing, I could calculate the Sun altitude, just at the calculated sunrise or sunset time, for any location. How ever, I leave that for you interested in the topic I’m not sure - do you want to test a bunch of hardcoded input-output pairs, or do you rather want to generate random inputs, run the tested function on those and assert something about the result? Both are possible

I have a plan how to do it, something like the first one you mentioned, comparing value-pairs [(calculated in elm expression, respective values of picked up from NOAA functions in Excel)] for a given argument within the list of the given range.
NOAA Solar Calculator is based on the well known Meeus algorithm. However, translating some of the lengthy Excel functions to Elm is quit prone to errors, so it needs some testing as much as possible.

Then you can use this approach to test large numbers of input-output pairs:

``````toTest : (Int, Int) -> Test
toTest ((input, expectedOutput) as case) =
Test.test (Debug.toString case) <|
\() ->
input
|> Foo.doSomething
|> Expect.equal expectedOutput

tests : Test
tests =
[(123,999)
,(159,438)
,...
]
|> List.map toTest
|> Test.describe "Foo tests"
``````
1 Like

Thank you Martin @Janiczek for your code!
It’s interesting and suitable for further developing ideas.
I wanted to modify it so that especially Float variables and functions could be tested. Here is the new example:
(Sorry, indentations are lost in the code below, I cannot fix it here but you can look at Ellie to see it better.)

All 8 tests are passed!

One question: The compiler claims in your code about
`toTest ((input, expectedOutput) as case).`
As I don’t know, why in fact, I changed the keyword case to testPoint.
Should there be something else instead?

``` module DecimalTests exposing (..) import Expect exposing (Expectation) import Test exposing (…) degToRad = \x → pi * x / 180.0 ```

```toTest : (Float, Float) → Test toTest ((input, expectedOutput) as testPoint) = Test.test (Debug.toString testPoint) <| () → input |> (-) expectedOutput |> abs |> Expect.lessThan 1.00e-5 tests : Test tests = [(pi, 3.14159) ,(degToRad 180, pi) ,(degToRad 90, pi / 2) ,(degToRad 60, pi / 3) ,(degToRad 45, pi / 4) ,(cos (pi/3), 0.50) ,( sin (pi/6), 0.50) ,(1111 / 1000, 1.111)] |> List.map toTest |> Test.describe “Trigonometric Functions Tests” ```

Ah, good point about the `case` - I wrote the code on my phone and didn’t try to run it. `case` is a reserved word so indeed you’d need to change it to something like `testPoint` as you did This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.