Unit Test Azure Function with Table Extension in C#
Tags: azure, tabl, mock, unittest
Published: 2022-06-13 14:04:54

## Table of Contents
1 [Functions](#functions)  
2 [Unit Tests](#"unittests)  

The new webjobs extensions can be used for [Azure Table Storage](https://docs.microsoft.com/en-us/azure/storage/tables/table-storage-overview) CRUD operations with Azure Functions:

Add nuget package to your Azure Function   
[nuget.org: Microsoft.Azure.WebJobs.Extensions.Tables](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.Tables)
```bash
dotnet add package Microsoft.Azure.WebJobs.Extensions.Tables
```

**Please find the complete code at [https://github.com/MarkusMeyer13/azure.functions.tables](https://github.com/MarkusMeyer13/azure.functions.tables)**


## 1 Functions<a id="functions"></a>

Using the [TableClient](https://docs.microsoft.com/en-us/dotnet/api/azure.data.tables.tableclient?view=azure-dotnet) is straight forward.

### AddEntityAsync
[TableEntity](https://docs.microsoft.com/en-us/dotnet/api/azure.data.tables.tableentity?view=azure-dotnet) with _TableClient_ :  

```csharp
[FunctionName("Add")]
public static async Task<IActionResult> AddAsync(
    [HttpTrigger(AuthorizationLevel.Function, "post", Route = "sample")] HttpRequest req,
    [Table("Sample", Connection = "SampleTableConnection")] TableClient client,
    ILogger log)
{
    var body = await req.ReadAsStringAsync().ConfigureAwait(false);

    var data = JObject.Parse(body);

    var entity = new TableEntity(data["Country"].Value<string>(), data["Company"].Value<string>())
    {
        ["Description"] = data["Description"].Value<string>()
    };

    var result = await client.AddEntityAsync(entity).ConfigureAwait(false);
    log.LogInformation($"Add - added");

    return new NoContentResult();
}
```
Request-Body:
```json
{
    "Country": "Germany",
    "Company": "Lorem",
    "Description": "Ipsum"
}
```


### DeleteEntityAsync
[TableEntity](https://docs.microsoft.com/en-us/dotnet/api/azure.data.tables.tableentity?view=azure-dotnet) with _PartitionKey_, _RowKey_ and _TableClient_:  

```csharp
[FunctionName("Delete")]
public static async Task<IActionResult> DeleteAsync(
    [HttpTrigger(AuthorizationLevel.Function, "delete", Route = "sample/{partitionKey}/{rowKey}")] HttpRequest req,
    [Table("Sample", Connection = "SampleTableConnection")] TableClient client,
    string partitionKey,
    string rowKey,
    ILogger log)
{
    var result = await client.DeleteEntityAsync(partitionKey, rowKey).ConfigureAwait(false);
    log.LogInformation($"Delete - StatusCode: {result.Status}");
    log.LogInformation($"Delete - Content: {result.Content}");

    if (result.Status.Equals(404))
    {
        log.LogInformation($"Delete - not found");
        return new NotFoundResult();
    }

    log.LogInformation($"Delete - deleted");

    return new NoContentResult();
}
```

## 2 Unit Tests<a id="unittests"></a>

The _TableClient_ and the _Azure.Response_ are [mocked](https://github.com/moq/moq4#moq):

```csharp
var mockResponse = new Mock<Azure.Response>();
mockResponse.SetupGet(x => x.Status).Returns((int)HttpStatusCode.NotFound);
mockResponse.SetupGet(x => x.Content).Returns(BinaryData.FromString("{\"odata.error\":{\"code\":\"ResourceNotFound\",\"message\":{\"lang\":\"en-US\",\"value\":\"The specified resource does not exist.\\nRequestId:19ab49c9-2002-001c-145f-7d77db000000\\nTime:2022-06-11T06:47:53.2893442Z\"}}}"));

Mock<TableClient> tableClient = new Mock<TableClient>();
tableClient.Setup(_ => _.DeleteEntityAsync(It.IsAny<string>(), It.IsAny<string>(), default, default))
    .ReturnsAsync(mockResponse.Object);

```
DeleteEntityAsync returns
- StatusCode: _404_ 
- Content: ```{"odata.error":{"code":"ResourceNotFound","message":{"lang":"en-US","value":"The specified resource does not exist.\nRequestId:19ab49c9-2002-001c-145f-7d77db000000\nTime:2022-06-11T06:47:53.2893442Z"}}}```  
if the entity is not found.

DeleteEntityAsync returns no content
- StatusCode: _204_
- Content:   

if the entity is found.