1+
2+ /**
3+ * 0320 Koan: "How do you know you're not a brain in a jar?"
4+ *
5+ * This one is messy at the moment, sorry. But ask yourself that
6+ * question. Mocking is all about asking that question for a function,
7+ * class, or method that you write.
8+ */
9+
10+
11+ export const providence_lat = 41.8240 ;
12+ export const providence_lon = - 71.4128 ;
13+
14+ interface TemperatureResult {
15+ success : boolean ,
16+ degrees ?: number ,
17+ scale ?: 'F' | 'C'
18+ }
19+
20+ /** Make an API call to the National Weather Service. I'm leaving out
21+ * Zod / careful validation here, because that's not the point of this
22+ * example. The point is that this function _calls a real web api_.
23+ */
24+ async function getRealTemperature ( lat : number , lon : number ) : Promise < TemperatureResult > {
25+ // As a demo, I'm using then/catch here instead of await.
26+ return await fetch ( `https://api.weather.gov/points/${ lat } ,${ lon } ` )
27+ . then ( res => {
28+ if ( ! res . ok ) return { success : false }
29+ return res . json ( ) ;
30+ } )
31+ . then ( data => {
32+ const forecastUrl = data . properties . forecast ;
33+ return fetch ( forecastUrl ) ;
34+ } )
35+ . then ( res => {
36+ if ( ! res . ok ) return { success : false }
37+ return res . json ( ) ;
38+ } )
39+ . then ( data => {
40+ const timePeriodsData = data ?. properties ?. periods ;
41+ // This isn't actually right; it'll return the temp
42+ // for the _next time period_, not the current temp.
43+ // See: https://github.com/tnelson/pvd-weather/blob/main/src/App.tsx
44+ //console.log(`${timePeriodsData[0].temperature}`)
45+ return { success : true ,
46+ degrees : timePeriodsData [ 0 ] . temperature ,
47+ scale : timePeriodsData [ 0 ] . temperatureUnit }
48+ } )
49+ . catch ( err => {
50+ return { success : false }
51+ } ) ;
52+ }
53+
54+ /** But what if we want to do something with that temperature? */
55+ async function weatherReportProvidence ( source : WeatherSource ) {
56+ const report = await source ( providence_lat , providence_lon )
57+ console . log ( `It's ${ report . degrees } ${ report . scale } out!` )
58+ }
59+
60+ /** Any function with this shape works as a weather source. */
61+ type WeatherSource = ( latarg : number , lonarg : number ) => Promise < TemperatureResult >
62+
63+ /** Build a "temperature source" that never actually asks the NWS, and just
64+ * returns the provided constant value. */
65+ function buildTemperatureMock ( temp : number , scale : 'F' | 'C' ) : WeatherSource {
66+ // This builder returns an async function indistinguishable from the real source.
67+ // (Note: we need the explicit return type here to get TypeScript to infer...)
68+ return async function ( lat : number , lon : number ) : Promise < TemperatureResult > {
69+ return { success : true , degrees : temp , scale : scale }
70+ }
71+ }
72+
73+ const alwaysSunny = buildTemperatureMock ( 72 , 'F' )
74+
75+ /** Note: don't try this outside of an async function.*/
76+ async function demo ( ) {
77+ // weatherReportProvidence can't tell the difference.
78+ console . log ( `real data:` )
79+ await weatherReportProvidence ( getRealTemperature )
80+ console . log ( `mock data:` )
81+ // for prototyping/testing
82+ await weatherReportProvidence ( alwaysSunny )
83+ }
84+ demo ( )
0 commit comments