Repository: getmoto/moto

Issue:
mock_dynamodb2 raises ValueError instead of ResourceNotFoundError in call to boto3.resource("dynamodb").batch_get_item() with an inexistent table
## Description

Specifying a DynamoDB table that does not exist in the `boto3.resource("dynamodb").batch_get_item()` function is expected to raise a `ResourceNotFoundException`. When using `mock_dynamodb2`, a `ValueError` is raised instead.

**Update**: Also occurs when calling the `batch_get_item` from a `boto3.client("dynamodb")` client, see details [below](https://github.com/spulec/moto/issues/4344#issuecomment-927172129).

## Steps to reproduce

Run the following test:

```python
import boto3
from moto import mock_dynamodb2
import pytest


@mock_dynamodb2
def test_batch_get_items_exception():

    ddb = boto3.resource("dynamodb")
    client = boto3.client("dynamodb")

    with pytest.raises(client.exceptions.ResourceNotFoundException):
        ddb.batch_get_item(RequestItems={"my-table": {"Keys": [{"id": 0}]}})

```

## Expected results

The test passes, i.e. a `ResourceNotFoundException` is raised.

## Actual results

A `ValueError` is raised:

```
$ python -m pytest -v tests/test_tests.py
======================================= test session starts ========================================
platform linux -- Python 3.8.10, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 -- /home/jul/git/dynamo-pandas/.venv/bin/python3
cachedir: .pytest_cache
Using --randomly-seed=4219719234
rootdir: /home/jul/git/dynamo-pandas, configfile: tox.ini
plugins: cov-2.12.1, randomly-3.8.0
collected 1 item                                                                                   

tests/test_tests.py::test_batch_get_items_exception FAILED                                   [100%]

============================================= FAILURES =============================================
__________________________________ test_batch_get_items_exception __________________________________

    @mock_dynamodb2
    def test_batch_get_items_exception():
    
        ddb = boto3.resource("dynamodb")
        client = boto3.client("dynamodb")
    
        with pytest.raises(client.exceptions.ResourceNotFoundException):
>           ddb.batch_get_item(RequestItems={"my-table": {"Keys": [{"id": 0}]}})

tests/test_tests.py:13: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.venv/lib/python3.8/site-packages/boto3/resources/factory.py:520: in do_action
    response = action(self, *args, **kwargs)
.venv/lib/python3.8/site-packages/boto3/resources/action.py:83: in __call__
    response = getattr(parent.meta.client, operation_name)(*args, **params)
.venv/lib/python3.8/site-packages/botocore/client.py:386: in _api_call
    return self._make_api_call(operation_name, kwargs)
.venv/lib/python3.8/site-packages/botocore/client.py:691: in _make_api_call
    http, parsed_response = self._make_request(
.venv/lib/python3.8/site-packages/botocore/client.py:711: in _make_request
    return self._endpoint.make_request(operation_model, request_dict)
.venv/lib/python3.8/site-packages/botocore/endpoint.py:102: in make_request
    return self._send_request(request_dict, operation_model)
.venv/lib/python3.8/site-packages/botocore/endpoint.py:136: in _send_request
    while self._needs_retry(attempts, operation_model, request_dict,
.venv/lib/python3.8/site-packages/botocore/endpoint.py:253: in _needs_retry
    responses = self._event_emitter.emit(
.venv/lib/python3.8/site-packages/botocore/hooks.py:357: in emit
    return self._emitter.emit(aliased_event_name, **kwargs)
.venv/lib/python3.8/site-packages/botocore/hooks.py:228: in emit
    return self._emit(event_name, kwargs)
.venv/lib/python3.8/site-packages/botocore/hooks.py:211: in _emit
    response = handler(**kwargs)
.venv/lib/python3.8/site-packages/botocore/retryhandler.py:183: in __call__
    if self._checker(attempts, response, caught_exception):
.venv/lib/python3.8/site-packages/botocore/retryhandler.py:250: in __call__
    should_retry = self._should_retry(attempt_number, response,
.venv/lib/python3.8/site-packages/botocore/retryhandler.py:269: in _should_retry
    return self._checker(attempt_number, response, caught_exception)
.venv/lib/python3.8/site-packages/botocore/retryhandler.py:316: in __call__
    checker_response = checker(attempt_number, response,
.venv/lib/python3.8/site-packages/botocore/retryhandler.py:222: in __call__
    return self._check_caught_exception(
.venv/lib/python3.8/site-packages/botocore/retryhandler.py:359: in _check_caught_exception
    raise caught_exception
.venv/lib/python3.8/site-packages/botocore/endpoint.py:197: in _do_get_response
    responses = self._event_emitter.emit(event_name, request=request)
.venv/lib/python3.8/site-packages/botocore/hooks.py:357: in emit
    return self._emitter.emit(aliased_event_name, **kwargs)
.venv/lib/python3.8/site-packages/botocore/hooks.py:228: in emit
    return self._emit(event_name, kwargs)
.venv/lib/python3.8/site-packages/botocore/hooks.py:211: in _emit
    response = handler(**kwargs)
.venv/lib/python3.8/site-packages/moto/core/models.py:398: in __call__
    status, headers, body = response_callback(
.venv/lib/python3.8/site-packages/moto/core/responses.py:205: in dispatch
    return cls()._dispatch(*args, **kwargs)
.venv/lib/python3.8/site-packages/moto/core/responses.py:315: in _dispatch
    return self.call_action()
.venv/lib/python3.8/site-packages/moto/core/utils.py:250: in _wrapper
    response = f(*args, **kwargs)
.venv/lib/python3.8/site-packages/moto/core/utils.py:282: in _wrapper
    response = f(*args, **kwargs)
.venv/lib/python3.8/site-packages/moto/dynamodb2/responses.py:138: in call_action
    response = getattr(self, endpoint)()
.venv/lib/python3.8/site-packages/moto/dynamodb2/responses.py:473: in batch_get_item
    item = self.dynamodb_backend.get_item(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <moto.dynamodb2.models.DynamoDBBackend object at 0x7f08470bd610>, table_name = 'my-table'
keys = {'id': {'N': '0'}}, projection_expression = None

    def get_item(self, table_name, keys, projection_expression=None):
        table = self.get_table(table_name)
        if not table:
>           raise ValueError("No table found")
E           ValueError: No table found

.venv/lib/python3.8/site-packages/moto/dynamodb2/models/__init__.py:1273: ValueError
===================================== short test summary info ======================================
FAILED tests/test_tests.py::test_batch_get_items_exception - ValueError: No table found
======================================== 1 failed in 0.40s =========================================
```

## Environment

Packages installed with pip in a virtual environment on Ubuntu 20.04.

```
boto3                             1.18.48
botocore                          1.21.48
moto                              2.2.7
```


Discussion:
Thanks for raising this and providing the minimum reproducible test @DrGFreeman - that's very helpful.
## Update

The issue also occurs when calling the `batch_get_item` from a `boto3.client()` instead of `boto3.resource()`. The following test can reproduce this condition:

```python
@mock_dynamodb2
def test_client_batch_get_item_exceptions():
    client = boto3.client("dynamodb")

    with pytest.raises(client.exceptions.ResourceNotFoundException):
        # Table my-table does not exist
        client.batch_get_item(
            RequestItems={"my-table": {"Keys": [{}]}}
        )
```

Resolving patch:
diff --git a/moto/dynamodb2/responses.py b/moto/dynamodb2/responses.py
--- a/moto/dynamodb2/responses.py
+++ b/moto/dynamodb2/responses.py
@@ -486,9 +486,15 @@ def batch_get_item(self):
 
             results["Responses"][table_name] = []
             for key in keys:
-                item = self.dynamodb_backend.get_item(
-                    table_name, key, projection_expression
-                )
+                try:
+                    item = self.dynamodb_backend.get_item(
+                        table_name, key, projection_expression
+                    )
+                except ValueError:
+                    return self.error(
+                        "com.amazonaws.dynamodb.v20111205#ResourceNotFoundException",
+                        "Requested resource not found",
+                    )
                 if item:
                     item_describe = item.describe_attrs(attributes_to_get)
                     results["Responses"][table_name].append(item_describe["Item"])


Test patch:
diff --git a/tests/test_dynamodb2/test_dynamodb_exceptions.py b/tests/test_dynamodb2/test_dynamodb_exceptions.py
--- a/tests/test_dynamodb2/test_dynamodb_exceptions.py
+++ b/tests/test_dynamodb2/test_dynamodb_exceptions.py
@@ -171,6 +171,18 @@ def test_update_item_range_key_set():
     )
 
 
+@mock_dynamodb2
+def test_batch_get_item_non_existing_table():
+
+    client = boto3.client("dynamodb", region_name="us-west-2")
+
+    with pytest.raises(client.exceptions.ResourceNotFoundException) as exc:
+        client.batch_get_item(RequestItems={"my-table": {"Keys": [{"id": {"N": "0"}}]}})
+    err = exc.value.response["Error"]
+    assert err["Code"].should.equal("ResourceNotFoundException")
+    assert err["Message"].should.equal("Requested resource not found")
+
+
 @mock_dynamodb2
 def test_batch_write_item_non_existing_table():
     client = boto3.client("dynamodb", region_name="us-west-2")