From ed3aab44fb924326b6215f2add3bfcea0a9ff81b Mon Sep 17 00:00:00 2001 From: VARUN1128 Date: Thu, 17 Jul 2025 21:44:35 +0530 Subject: [PATCH] Improve unit test coverage for NLU pipeline components --- .../intent_classifiers/tf_intent_classifer.py | 5 +- tests/__init__.py | 3 + tests/test_nlu_pipeline.py | 82 +++++++++++++++++++ 3 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 tests/test_nlu_pipeline.py diff --git a/app/bot/nlu/intent_classifiers/tf_intent_classifer.py b/app/bot/nlu/intent_classifiers/tf_intent_classifer.py index dcc0c841..65b496d9 100755 --- a/app/bot/nlu/intent_classifiers/tf_intent_classifer.py +++ b/app/bot/nlu/intent_classifiers/tf_intent_classifer.py @@ -7,9 +7,8 @@ import spacy import tensorflow as tf from sklearn.preprocessing import LabelBinarizer -from tensorflow.python.keras import Sequential -from tensorflow.python.layers.core import Dense -from tensorflow.python.layers.core import Dropout +from tensorflow.keras import Sequential +from tensorflow.keras.layers import Dense, Dropout from app.bot.nlu.pipeline import NLUComponent np.random.seed(1) diff --git a/tests/__init__.py b/tests/__init__.py index e69de29b..b28b04f6 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -0,0 +1,3 @@ + + + diff --git a/tests/test_nlu_pipeline.py b/tests/test_nlu_pipeline.py new file mode 100644 index 00000000..66da5f35 --- /dev/null +++ b/tests/test_nlu_pipeline.py @@ -0,0 +1,82 @@ +import pytest +from unittest.mock import MagicMock, patch +from app.bot.nlu.featurizers.spacy_featurizer import SpacyFeaturizer +from app.bot.nlu.intent_classifiers.sklearn_intent_classifer import SklearnIntentClassifier +from app.bot.nlu.intent_classifiers.tf_intent_classifer import TfIntentClassifier +from app.bot.nlu.entity_extractors.crf_entity_extractor import CRFEntityExtractor +from app.bot.nlu.entity_extractors.synonym_replacer import SynonymReplacer +from app.bot.nlu.llm.zero_shot_nlu_openai import ZeroShotNLUOpenAI + +@pytest.fixture +def sample_text(): + return "Order a large pepperoni pizza" + +@pytest.fixture +def spacy_model(): + import spacy + return spacy.blank("en") + +@pytest.fixture +def spacy_featurizer(spacy_model): + return SpacyFeaturizer(model_name="en") + + +def test_spacy_featurizer_process(spacy_featurizer, sample_text): + message = {"text": sample_text} + result = spacy_featurizer.process(message) + assert "spacy_doc" in result + assert result["spacy_doc"].text == sample_text + + +def test_sklearn_intent_classifier_process(): + clf = SklearnIntentClassifier() + clf.model = MagicMock() + clf.model.classes_ = ["order_pizza", "greet"] + clf.model.predict_proba.return_value = [[0.8, 0.2]] + message = {"text": "Order pizza", "spacy_doc": MagicMock(vector=[1.0, 2.0])} + result = clf.process(message) + assert "intent" in result + assert "intent_ranking" in result + + +def test_tf_intent_classifier_process(): + clf = TfIntentClassifier() + clf.model = MagicMock() + clf.label_encoder = MagicMock() + clf.label_encoder.classes_ = ["order_pizza", "greet"] + clf.graph = MagicMock() + clf.model.predict.return_value = [[0.7, 0.3]] + message = {"text": "Order pizza"} + with patch.object(clf, "nlp", MagicMock()): + result = clf.process(message) + assert "intent" in result + assert "intent_ranking" in result + + +def test_crf_entity_extractor_process(): + extractor = CRFEntityExtractor() + extractor.tagger = MagicMock() + extractor.tagger.tag.return_value = ["B-pizza_size", "O", "B-pizza_topping"] + message = {"text": "large pepperoni", "spacy_doc": MagicMock()} + with patch.object(extractor, "pos_tagger", return_value=[("large", "JJ"), ("pepperoni", "NN")]): + with patch.object(extractor, "sent_to_features", return_value=[["feature1"], ["feature2"]]): + result = extractor.process(message) + assert "entities" in result + + +def test_synonym_replacer_process(): + synonyms = {"big": "large"} + replacer = SynonymReplacer(synonyms=synonyms) + message = {"entities": {"pizza_size": "big"}} + result = replacer.process(message) + assert result["entities"]["pizza_size"] == "large" + + +def test_zero_shot_nlu_openai_process(): + zsnlu = ZeroShotNLUOpenAI(intents=["order_pizza"], entities=["pizza_size"]) + zsnlu.chain = MagicMock() + zsnlu.chain.invoke.return_value = {"intent": "order_pizza", "entities": {"pizza_size": "large"}} + message = {"text": "I want a large pizza"} + result = zsnlu.process(message) + assert result["intent"]["intent"] == "order_pizza" + assert result["entities"]["pizza_size"] == "large" \ No newline at end of file