現在日時もDI（依存性注入）してテスト可能にする

`DateTime` を使用するコードをテスト可能なイイ感じにします。

## テストができないコード

`DateTime` が挙動にかかわるとユニットテストがしにくくなります。
時刻に応じて挨拶を返すサービスクラスを例に考えてみます。

```csharp:Services/GreetingService.cs
public class GreetingService
{
    public string Greet(string name)
    {
        var time = DateTime.Now.TimeOfDay;
        if (new TimeSpan(5, 00, 00) <= time && time < new TimeSpan(10, 00, 00))
            return $"おはよう！{name}さん！";
        else if (new TimeSpan(10,00,00) <= time && time < new TimeSpan(17, 00, 00))
            return $"こんにちは！{name}さん！";
        else
            return $"こんばんは！{name}さん！";
    }
}
```
現在時刻に応じて正しく文字列が返るかテストがしたいけど、できない...:cry:
これでは幸せになれません...。

## DI（依存性注入）できるようにリファクタリング

dependency injection（依存性注入）はとても大雑把に説明すると、依存するオブジェクトをクラスの外から注入することです。
外から注入することで、動的に動作を変えられるようになります。

- [猿でも分かる! Dependency Injection: 依存性の注入](https://qiita.com/hshimo/items/1136087e1c6e5c5b0d9f)
- [DI・DIコンテナ、ちゃんと理解出来てる・・？](https://qiita.com/ritukiii/items/de30b2d944109521298f)

今回の場合、 `DateTime` が `GreetingService` の外から変えられればいいわけですね。

まず、`IDateTime` インタフェースを定義します。
DI の詳細の記事ではないので省略しますが、実装を隠ぺいするためです。
`GreetingService` が具体的な実装ではなく、 `IDateTime` インタフェースのみに依存するようにします。

```csharp:Interfaces/IDateTime.cs
public interface IDateTime
{
    DateTime Now { get; }
}
```

次に `IDateTime` を実装したクラスを作成します。
今回はサーバーシステムの現在時刻を返すクラスを作成します。

```csharp:Services/ServerDateTime
public class ServerDateTime : IDateTime
{
    public DateTime Now => DateTime.Now;
}
````

最後に `GreetingService` をリファクタします。
`IDateTime` を実装したクラスをコンストラクターの引数を通じて受け取るだけです。


```c#:Services/GreetingService.cs
public class GreetingService
{
    private readonly IDateTime _dateTime;

    public GreetingService(IDateTime dateTime)
        => _dateTime = dateTime;

    public string Greet(string name)
    {
        var time = _dateTime.Now.TimeOfDay;
        if (new TimeSpan(5, 00, 00) <= time && time < new TimeSpan(10, 00, 00))
            return $"おはよう！{name}さん！";
        else if (new TimeSpan(10,00,00) <= time && time < new TimeSpan(17, 00, 00))
            return $"こんにちは！{name}さん！";
        else
            return $"こんばんは！{name}さん！";
    }
}
```

インターフェースを使用したことで、実装を簡単に差し替えることができます。
`IDateTime` を実装したクラスなら DB 時刻を返す `DatabaseDateTime` でもなんでも DI できます。:grinning:
後述する ASP.NET Core のビルトイン DI コンテナーやモックライブラリ `Moq` もインターフェースを使用します。


## ユニットテストする！

xUnit でテストを行いました。
`Moq` でモックを用意しましたが、`IDateTime` が単純なのでフェイククラスを作成してもＯＫかと。

```csharp:GreetingServiceTest.cs
public class GreetingServiceTest
{
    [Fact]
    public void Returns_valid_string_at_0500()
    {
        // Arrange
        var dateTime = new Mock<IDateTime>();
        dateTime.Setup(d => d.Now)
            .Returns(DateTime.Parse("2018-04-14 05:00:00"));

        var sut = new GreetingService(dateTime.Object);
        var name = "John Doe";

        // Act
        var greeting = sut.Greet(name);

        // Assert
        Assert.Equal($"おはよう！{name}さん！", greeting);
    }
}
```

時刻を簡単に変更してテストでき、幸せになれました:blush:

## 実際に使ってみる

DI できるようになったので ASP.NET Core のビルトイン DI コンテナーで使用してみます。
`GreetingService` も DI できるようにインターフェースを実装し、 API の Controller クラスで利用します。

```csharp:Interfaces/IGreeting.cs
public interface IGreeting
{
    string Greet(string name);
}
```

```csharp:Services/GreetingService.cs
public class GreetingService : IGreeting
{
    :
    :
}
```

`Startup.cs` の `ConfigureServices` メソッドで DI コンテナに登録します。

```csharp:Startup.cs
public void ConfigureServices(IServiceCollection services) => 
    services.AddTransient<IDateTime, ServerDateTime>()
        .AddTransient<IGreeting, GreetingService>()
        .AddMvc();
```

それでは実際に Web API で使ってみます。

```csharp:GreetingController.cs
[Route("api/[controller]")]
public class GreetingController : Controller
{
    private readonly IGreeting _greetingServie;

    public GreetingController(IGreeting greetingService) =>
        _greetingServie = greetingService;

    [HttpGet("{name}")]
    public string Get(string name) =>
        _greetingServie.Greet(name);
}
```

バッチリ動きました！:grin:

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195024/49a4621f-086b-63bd-15bf-444601c59be3.png)