mirror of
https://github.com/semantica-agi/semantica.git
synced 2026-08-29 04:26:20 +00:00
test(visualization): isolate optional dependency mocks (#897)
* test(visualization): isolate optional dependency mocks * test(visualization): stop requiring Plotly in unit tests Removing the global sys.modules stubs left the tests that patch `...go.Bar`, or call a visualizer, with nothing standing in for the module level `px` and `go` aliases. Those are None when Plotly is missing, so patch resolution and _check_dependencies() both failed. Add a helper that substitutes a double only for the aliases that are None, leaving the real module in place when Plotly is installed. ---------
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
"""Shared helper for visualization tests.
|
||||
|
||||
The visualization modules treat Plotly as optional: they bind ``px``, ``go`` and
|
||||
``make_subplots`` to ``None`` when the import fails, and raise ``ProcessingError``
|
||||
from ``_check_dependencies()``. Tests that exercise a Plotly-backed path need
|
||||
those names to be usable, otherwise ``patch("...go.Figure")`` fails on ``None``
|
||||
and the visualizers refuse to run.
|
||||
|
||||
``plotly_doubles`` fills in a double for each alias that is ``None``, so the
|
||||
tests describe their own requirements instead of depending on whether Plotly
|
||||
happens to be installed. When Plotly is installed the aliases are left alone and
|
||||
the patches keep asserting against the real attribute names.
|
||||
"""
|
||||
|
||||
from contextlib import ExitStack, contextmanager
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
PLOTLY_ALIASES = ("px", "go", "make_subplots")
|
||||
|
||||
|
||||
@contextmanager
|
||||
def plotly_doubles(*modules):
|
||||
"""Stand in for the module-level Plotly aliases that are unavailable."""
|
||||
with ExitStack() as stack:
|
||||
for module in modules:
|
||||
for alias in PLOTLY_ALIASES:
|
||||
if getattr(module, alias, "unused") is None:
|
||||
stack.enter_context(patch.object(module, alias, MagicMock()))
|
||||
yield
|
||||
@@ -9,27 +9,16 @@ and must raise a clear ProcessingError for anything else.
|
||||
"""
|
||||
|
||||
import contextlib
|
||||
import sys
|
||||
import unittest
|
||||
from dataclasses import dataclass, field
|
||||
from typing import List
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Stub out heavy optional deps before importing the module under test
|
||||
# ---------------------------------------------------------------------------
|
||||
sys.modules.setdefault("matplotlib", MagicMock())
|
||||
sys.modules.setdefault("matplotlib.pyplot", MagicMock())
|
||||
sys.modules.setdefault("matplotlib.patches", MagicMock())
|
||||
sys.modules.setdefault("plotly", MagicMock())
|
||||
sys.modules.setdefault("plotly.express", MagicMock())
|
||||
sys.modules.setdefault("plotly.graph_objects", MagicMock())
|
||||
sys.modules.setdefault("plotly.subplots", MagicMock())
|
||||
sys.modules.setdefault("graphviz", MagicMock())
|
||||
sys.modules.setdefault("seaborn", MagicMock())
|
||||
|
||||
from semantica.utils.exceptions import ProcessingError # noqa: E402
|
||||
from semantica.visualization import kg_visualizer # noqa: E402
|
||||
from semantica.visualization.kg_visualizer import KGVisualizer # noqa: E402
|
||||
from tests.visualization._plotly_doubles import plotly_doubles # noqa: E402
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Minimal fixtures
|
||||
@@ -191,10 +180,6 @@ class TestVisualizeNetworkAcceptsKGObject(unittest.TestCase):
|
||||
def _run_visualize_network(self, graph_arg):
|
||||
"""Run visualize_network with all Plotly internals mocked."""
|
||||
mock_fig = MagicMock()
|
||||
mock_go = sys.modules["plotly.graph_objects"]
|
||||
mock_go.Figure.return_value = mock_fig
|
||||
mock_go.Scatter.return_value = MagicMock()
|
||||
mock_go.Layout.return_value = MagicMock()
|
||||
|
||||
viz = _make_viz()
|
||||
|
||||
@@ -207,6 +192,10 @@ class TestVisualizeNetworkAcceptsKGObject(unittest.TestCase):
|
||||
|
||||
# ColorPalette helpers
|
||||
with (
|
||||
plotly_doubles(kg_visualizer),
|
||||
patch("semantica.visualization.kg_visualizer.go.Figure", return_value=mock_fig),
|
||||
patch("semantica.visualization.kg_visualizer.go.Scatter"),
|
||||
patch("semantica.visualization.kg_visualizer.go.Layout"),
|
||||
patch(
|
||||
"semantica.visualization.kg_visualizer.ColorPalette.get_entity_type_colors",
|
||||
return_value={"Person": "#ff0000"},
|
||||
@@ -254,9 +243,12 @@ class TestAllVisualizeMethodsAcceptKGObject(unittest.TestCase):
|
||||
self.viz._normalize_graph = MagicMock(return_value=GRAPH_DICT)
|
||||
self.viz._visualize_network_plotly = MagicMock(return_value=MagicMock())
|
||||
communities = {"node_assignments": {"e1": 0, "e2": 1}, "num_communities": 2}
|
||||
with patch(
|
||||
"semantica.visualization.kg_visualizer.ColorPalette.get_community_colors",
|
||||
return_value=["#ff0000", "#00ff00"],
|
||||
with (
|
||||
plotly_doubles(kg_visualizer),
|
||||
patch(
|
||||
"semantica.visualization.kg_visualizer.ColorPalette.get_community_colors",
|
||||
return_value=["#ff0000", "#00ff00"],
|
||||
),
|
||||
):
|
||||
self.viz.visualize_communities(self.kg, communities=communities)
|
||||
self.viz._normalize_graph.assert_called_once_with(self.kg)
|
||||
@@ -264,22 +256,24 @@ class TestAllVisualizeMethodsAcceptKGObject(unittest.TestCase):
|
||||
def test_visualize_centrality_accepts_kg_object(self):
|
||||
self.viz._normalize_graph = MagicMock(return_value=GRAPH_DICT)
|
||||
self.viz._visualize_network_plotly = MagicMock(return_value=MagicMock())
|
||||
self.viz.visualize_centrality(self.kg, centrality={"centrality": {}})
|
||||
with plotly_doubles(kg_visualizer):
|
||||
self.viz.visualize_centrality(self.kg, centrality={"centrality": {}})
|
||||
self.viz._normalize_graph.assert_called_once_with(self.kg)
|
||||
|
||||
def test_visualize_entity_types_accepts_kg_object(self):
|
||||
self.viz._normalize_graph = MagicMock(return_value=GRAPH_DICT)
|
||||
mock_px = sys.modules["plotly.express"]
|
||||
mock_px.bar.return_value = MagicMock()
|
||||
self.viz.visualize_entity_types(self.kg)
|
||||
with plotly_doubles(kg_visualizer), patch("semantica.visualization.kg_visualizer.px.bar"):
|
||||
self.viz.visualize_entity_types(self.kg)
|
||||
self.viz._normalize_graph.assert_called_once_with(self.kg)
|
||||
|
||||
def test_visualize_relationship_matrix_accepts_kg_object(self):
|
||||
self.viz._normalize_graph = MagicMock(return_value=GRAPH_DICT)
|
||||
mock_go = sys.modules["plotly.graph_objects"]
|
||||
mock_go.Figure.return_value = MagicMock()
|
||||
mock_go.Heatmap.return_value = MagicMock()
|
||||
self.viz.visualize_relationship_matrix(self.kg)
|
||||
with (
|
||||
plotly_doubles(kg_visualizer),
|
||||
patch("semantica.visualization.kg_visualizer.go.Figure"),
|
||||
patch("semantica.visualization.kg_visualizer.go.Heatmap"),
|
||||
):
|
||||
self.viz.visualize_relationship_matrix(self.kg)
|
||||
self.viz._normalize_graph.assert_called_once_with(self.kg)
|
||||
|
||||
|
||||
@@ -361,10 +355,6 @@ class TestFormalKnowledgeGraphType(unittest.TestCase):
|
||||
|
||||
def _run_visualize_network(self, graph_arg):
|
||||
mock_fig = MagicMock()
|
||||
mock_go = sys.modules["plotly.graph_objects"]
|
||||
mock_go.Figure.return_value = mock_fig
|
||||
mock_go.Scatter.return_value = MagicMock()
|
||||
mock_go.Layout.return_value = MagicMock()
|
||||
viz = _make_viz()
|
||||
fake_pos = {"e1": (0.0, 0.0), "e2": (1.0, 1.0)}
|
||||
viz.force_layout = MagicMock()
|
||||
@@ -372,6 +362,10 @@ class TestFormalKnowledgeGraphType(unittest.TestCase):
|
||||
viz.hierarchical_layout = MagicMock()
|
||||
viz.circular_layout = MagicMock()
|
||||
with (
|
||||
plotly_doubles(kg_visualizer),
|
||||
patch("semantica.visualization.kg_visualizer.go.Figure", return_value=mock_fig),
|
||||
patch("semantica.visualization.kg_visualizer.go.Scatter"),
|
||||
patch("semantica.visualization.kg_visualizer.go.Layout"),
|
||||
patch(
|
||||
"semantica.visualization.kg_visualizer.ColorPalette.get_entity_type_colors",
|
||||
return_value={"Person": "#ff0000"},
|
||||
@@ -392,9 +386,12 @@ class TestFormalKnowledgeGraphType(unittest.TestCase):
|
||||
viz._normalize_graph = MagicMock(return_value=GRAPH_DICT)
|
||||
viz._visualize_network_plotly = MagicMock(return_value=MagicMock())
|
||||
communities = {"node_assignments": {"e1": 0, "e2": 1}, "num_communities": 2}
|
||||
with patch(
|
||||
"semantica.visualization.kg_visualizer.ColorPalette.get_community_colors",
|
||||
return_value=["#ff0000", "#00ff00"],
|
||||
with (
|
||||
plotly_doubles(kg_visualizer),
|
||||
patch(
|
||||
"semantica.visualization.kg_visualizer.ColorPalette.get_community_colors",
|
||||
return_value=["#ff0000", "#00ff00"],
|
||||
),
|
||||
):
|
||||
viz.visualize_communities(kg, communities=communities)
|
||||
viz._normalize_graph.assert_called_once_with(kg)
|
||||
@@ -404,24 +401,28 @@ class TestFormalKnowledgeGraphType(unittest.TestCase):
|
||||
viz = _make_viz()
|
||||
viz._normalize_graph = MagicMock(return_value=GRAPH_DICT)
|
||||
viz._visualize_network_plotly = MagicMock(return_value=MagicMock())
|
||||
viz.visualize_centrality(kg, centrality={"centrality": {}})
|
||||
with plotly_doubles(kg_visualizer):
|
||||
viz.visualize_centrality(kg, centrality={"centrality": {}})
|
||||
viz._normalize_graph.assert_called_once_with(kg)
|
||||
|
||||
def test_visualize_entity_types_accepts_knowledge_graph(self):
|
||||
kg = self._make_kg()
|
||||
viz = _make_viz()
|
||||
viz._normalize_graph = MagicMock(return_value=GRAPH_DICT)
|
||||
sys.modules["plotly.express"].bar.return_value = MagicMock()
|
||||
viz.visualize_entity_types(kg)
|
||||
with plotly_doubles(kg_visualizer), patch("semantica.visualization.kg_visualizer.px.bar"):
|
||||
viz.visualize_entity_types(kg)
|
||||
viz._normalize_graph.assert_called_once_with(kg)
|
||||
|
||||
def test_visualize_relationship_matrix_accepts_knowledge_graph(self):
|
||||
kg = self._make_kg()
|
||||
viz = _make_viz()
|
||||
viz._normalize_graph = MagicMock(return_value=GRAPH_DICT)
|
||||
sys.modules["plotly.graph_objects"].Figure.return_value = MagicMock()
|
||||
sys.modules["plotly.graph_objects"].Heatmap.return_value = MagicMock()
|
||||
viz.visualize_relationship_matrix(kg)
|
||||
with (
|
||||
plotly_doubles(kg_visualizer),
|
||||
patch("semantica.visualization.kg_visualizer.go.Figure"),
|
||||
patch("semantica.visualization.kg_visualizer.go.Heatmap"),
|
||||
):
|
||||
viz.visualize_relationship_matrix(kg)
|
||||
viz._normalize_graph.assert_called_once_with(kg)
|
||||
|
||||
def test_knowledge_graph_importable_from_kg_module(self):
|
||||
|
||||
@@ -1,149 +1,115 @@
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, patch
|
||||
import importlib
|
||||
import sys
|
||||
import unittest
|
||||
from contextlib import contextmanager
|
||||
from unittest.mock import patch
|
||||
|
||||
import numpy as np
|
||||
|
||||
# Helper to mock modules
|
||||
def mock_module(name):
|
||||
m = MagicMock()
|
||||
sys.modules[name] = m
|
||||
return m
|
||||
from tests.visualization._plotly_doubles import plotly_doubles
|
||||
|
||||
|
||||
@contextmanager
|
||||
def import_without(module_name, *dependencies):
|
||||
"""Import a module with selected optional dependencies unavailable."""
|
||||
package_name, attribute = module_name.rsplit(".", 1)
|
||||
package = importlib.import_module(package_name)
|
||||
missing = object()
|
||||
original_module = sys.modules.pop(module_name, missing)
|
||||
original_attribute = getattr(package, attribute, missing)
|
||||
|
||||
try:
|
||||
with patch.dict(sys.modules, {name: None for name in dependencies}):
|
||||
yield importlib.import_module(module_name)
|
||||
finally:
|
||||
sys.modules.pop(module_name, None)
|
||||
if original_module is not missing:
|
||||
sys.modules[module_name] = original_module
|
||||
if original_attribute is missing:
|
||||
package.__dict__.pop(attribute, None)
|
||||
else:
|
||||
setattr(package, attribute, original_attribute)
|
||||
|
||||
|
||||
class TestOptionalDependencies(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
# Mock heavy/problematic dependencies globally to prevent environment crashes
|
||||
# We use a dict to save original modules if they exist, but for this test file
|
||||
# we generally want to run in a controlled "clean" environment.
|
||||
cls.modules_to_patch = [
|
||||
'sklearn', 'sklearn.decomposition', 'sklearn.manifold',
|
||||
'scipy', 'scipy.optimize',
|
||||
'matplotlib', 'matplotlib.pyplot', 'matplotlib.patches',
|
||||
'plotly', 'plotly.express', 'plotly.graph_objects', 'plotly.subplots',
|
||||
'networkx', 'seaborn'
|
||||
]
|
||||
|
||||
cls.original_modules = {}
|
||||
for mod in cls.modules_to_patch:
|
||||
if mod in sys.modules:
|
||||
cls.original_modules[mod] = sys.modules[mod]
|
||||
sys.modules[mod] = MagicMock()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
# Restore original modules
|
||||
for mod in cls.modules_to_patch:
|
||||
if mod in cls.original_modules:
|
||||
sys.modules[mod] = cls.original_modules[mod]
|
||||
else:
|
||||
del sys.modules[mod]
|
||||
|
||||
def setUp(self):
|
||||
# Clear cached visualization modules to ensure fresh imports
|
||||
self.viz_modules = [
|
||||
'semantica.visualization.embedding_visualizer',
|
||||
'semantica.visualization.ontology_visualizer',
|
||||
'semantica.visualization.kg_visualizer',
|
||||
'semantica.visualization.utils.export_formats'
|
||||
]
|
||||
for mod in self.viz_modules:
|
||||
if mod in sys.modules:
|
||||
del sys.modules[mod]
|
||||
|
||||
def test_embedding_visualizer_without_umap(self):
|
||||
"""Test EmbeddingVisualizer behavior when umap is missing."""
|
||||
# Ensure umap is missing
|
||||
with patch.dict(sys.modules, {'umap': None}):
|
||||
from semantica.visualization.embedding_visualizer import EmbeddingVisualizer
|
||||
with import_without(
|
||||
"semantica.visualization.embedding_visualizer", "umap"
|
||||
) as module:
|
||||
with plotly_doubles(module), patch.object(module, "PCA") as mock_pca_class:
|
||||
mock_pca_class.return_value.fit_transform.return_value = np.zeros((4, 2))
|
||||
|
||||
# Setup PCA mock to verify fallback
|
||||
mock_pca_class = sys.modules['sklearn.decomposition'].PCA
|
||||
mock_pca_instance = mock_pca_class.return_value
|
||||
# Configure fit_transform to return correct shape (n_samples, 2)
|
||||
mock_pca_instance.fit_transform.return_value = np.zeros((4, 2))
|
||||
viz = module.EmbeddingVisualizer()
|
||||
embeddings = np.array([[0, 1, 2], [1, 0, 3], [0, 0, 0], [1, 1, 1]])
|
||||
viz.visualize_2d_projection(embeddings, method="umap")
|
||||
|
||||
viz = EmbeddingVisualizer()
|
||||
# Use numpy array!
|
||||
embeddings = np.array([[0, 1, 2], [1, 0, 3], [0, 0, 0], [1, 1, 1]])
|
||||
|
||||
# Should fallback to PCA when method="umap" is used but umap is None
|
||||
# The code logs a warning and uses PCA
|
||||
viz.visualize_2d_projection(embeddings, method="umap")
|
||||
|
||||
# Verify PCA was called
|
||||
mock_pca_class.assert_called()
|
||||
|
||||
def test_ontology_visualizer_without_graphviz(self):
|
||||
"""Test OntologyVisualizer behavior when graphviz is missing."""
|
||||
# Ensure graphviz is missing
|
||||
with patch.dict(sys.modules, {'graphviz': None}):
|
||||
from semantica.visualization.ontology_visualizer import OntologyVisualizer, ProcessingError
|
||||
|
||||
viz = OntologyVisualizer()
|
||||
with import_without(
|
||||
"semantica.visualization.ontology_visualizer", "graphviz"
|
||||
) as module:
|
||||
viz = module.OntologyVisualizer()
|
||||
ontology = {
|
||||
"classes": [
|
||||
{"name": "A", "label": "A"},
|
||||
{"name": "B", "label": "B", "parent": "A"}
|
||||
{"name": "B", "label": "B", "parent": "A"},
|
||||
]
|
||||
}
|
||||
|
||||
with self.assertRaises(ProcessingError) as cm:
|
||||
with self.assertRaises(module.ProcessingError) as cm:
|
||||
viz.visualize_hierarchy(ontology, output="dot", file_path="test.dot")
|
||||
|
||||
self.assertIn("Graphviz is required for DOT export", str(cm.exception))
|
||||
|
||||
def test_analytics_visualizer_without_plotly(self):
|
||||
"""Test AnalyticsVisualizer behavior when plotly is missing."""
|
||||
with patch.dict(sys.modules, {'plotly': None, 'plotly.express': None, 'plotly.graph_objects': None}):
|
||||
from semantica.visualization.analytics_visualizer import AnalyticsVisualizer, ProcessingError
|
||||
with import_without(
|
||||
"semantica.visualization.analytics_visualizer",
|
||||
"plotly",
|
||||
"plotly.express",
|
||||
"plotly.graph_objects",
|
||||
) as module:
|
||||
viz = module.AnalyticsVisualizer()
|
||||
|
||||
# Need to ensure numpy is available for init (it's imported at top level)
|
||||
# But we are testing plotly missing.
|
||||
|
||||
viz = AnalyticsVisualizer()
|
||||
|
||||
with self.assertRaises(ProcessingError) as cm:
|
||||
viz.visualize_centrality_rankings({"node1": 1.0})
|
||||
|
||||
self.assertIn("Plotly is required", str(cm.exception))
|
||||
|
||||
def test_analytics_visualizer_without_plotly(self):
|
||||
"""Test AnalyticsVisualizer behavior when plotly is missing."""
|
||||
with patch.dict(sys.modules, {'plotly': None, 'plotly.express': None, 'plotly.graph_objects': None}):
|
||||
from semantica.visualization.analytics_visualizer import AnalyticsVisualizer, ProcessingError
|
||||
|
||||
viz = AnalyticsVisualizer()
|
||||
|
||||
with self.assertRaises(ProcessingError) as cm:
|
||||
with self.assertRaises(module.ProcessingError) as cm:
|
||||
viz.visualize_centrality_rankings({})
|
||||
|
||||
self.assertIn("Plotly is required", str(cm.exception))
|
||||
|
||||
def test_semantic_network_visualizer_without_plotly(self):
|
||||
"""Test SemanticNetworkVisualizer behavior when plotly is missing."""
|
||||
with patch.dict(sys.modules, {'plotly': None, 'plotly.express': None, 'plotly.graph_objects': None}):
|
||||
from semantica.visualization.semantic_network_visualizer import SemanticNetworkVisualizer, ProcessingError
|
||||
with import_without(
|
||||
"semantica.visualization.semantic_network_visualizer",
|
||||
"plotly",
|
||||
"plotly.express",
|
||||
"plotly.graph_objects",
|
||||
) as module:
|
||||
viz = module.SemanticNetworkVisualizer()
|
||||
|
||||
viz = SemanticNetworkVisualizer()
|
||||
|
||||
with self.assertRaises(ProcessingError) as cm:
|
||||
with self.assertRaises(module.ProcessingError) as cm:
|
||||
viz.visualize_network({})
|
||||
|
||||
self.assertIn("Plotly is required", str(cm.exception))
|
||||
|
||||
def test_temporal_visualizer_without_plotly(self):
|
||||
"""Test TemporalVisualizer behavior when plotly is missing."""
|
||||
with patch.dict(sys.modules, {'plotly': None, 'plotly.express': None, 'plotly.graph_objects': None}):
|
||||
from semantica.visualization.temporal_visualizer import TemporalVisualizer, ProcessingError
|
||||
with import_without(
|
||||
"semantica.visualization.temporal_visualizer",
|
||||
"plotly",
|
||||
"plotly.express",
|
||||
"plotly.graph_objects",
|
||||
) as module:
|
||||
viz = module.TemporalVisualizer()
|
||||
|
||||
viz = TemporalVisualizer()
|
||||
|
||||
with self.assertRaises(ProcessingError) as cm:
|
||||
with self.assertRaises(module.ProcessingError) as cm:
|
||||
viz.visualize_timeline({"events": []})
|
||||
|
||||
self.assertIn("Plotly is required", str(cm.exception))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -1,27 +1,5 @@
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, patch, ANY
|
||||
import sys
|
||||
import types
|
||||
|
||||
# Helper to create a mock package
|
||||
def mock_package(name):
|
||||
m = MagicMock()
|
||||
m.__path__ = []
|
||||
sys.modules[name] = m
|
||||
return m
|
||||
|
||||
# Mock libraries before importing module under test
|
||||
# We need to ensure matplotlib behaves like a package for seaborn
|
||||
sys.modules['matplotlib'] = MagicMock()
|
||||
sys.modules['matplotlib.colors'] = MagicMock()
|
||||
sys.modules['matplotlib.pyplot'] = MagicMock()
|
||||
sys.modules['matplotlib.patches'] = MagicMock()
|
||||
sys.modules['plotly'] = MagicMock()
|
||||
sys.modules['plotly.express'] = MagicMock()
|
||||
sys.modules['plotly.graph_objects'] = MagicMock()
|
||||
sys.modules['plotly.subplots'] = MagicMock()
|
||||
sys.modules['graphviz'] = MagicMock()
|
||||
sys.modules['seaborn'] = MagicMock()
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from semantica.visualization.kg_visualizer import KGVisualizer
|
||||
from semantica.visualization.ontology_visualizer import OntologyVisualizer
|
||||
|
||||
@@ -1,27 +1,14 @@
|
||||
|
||||
import unittest
|
||||
from contextlib import ExitStack
|
||||
from unittest.mock import MagicMock, patch
|
||||
import sys
|
||||
import numpy as np
|
||||
|
||||
# Mock heavy libraries before importing visualization modules
|
||||
sys.modules['matplotlib'] = MagicMock()
|
||||
sys.modules['matplotlib.pyplot'] = MagicMock()
|
||||
sys.modules['matplotlib.colors'] = MagicMock()
|
||||
sys.modules['matplotlib.patches'] = MagicMock()
|
||||
sys.modules['plotly'] = MagicMock()
|
||||
sys.modules['plotly.express'] = MagicMock()
|
||||
sys.modules['plotly.graph_objects'] = MagicMock()
|
||||
sys.modules['plotly.subplots'] = MagicMock()
|
||||
sys.modules['seaborn'] = MagicMock()
|
||||
sys.modules['umap'] = MagicMock()
|
||||
sys.modules['sklearn'] = MagicMock()
|
||||
sys.modules['sklearn.decomposition'] = MagicMock()
|
||||
sys.modules['sklearn.manifold'] = MagicMock()
|
||||
|
||||
from semantica.visualization import analytics_visualizer, embedding_visualizer
|
||||
from semantica.visualization.analytics_visualizer import AnalyticsVisualizer
|
||||
from semantica.visualization.embedding_visualizer import EmbeddingVisualizer
|
||||
from semantica.visualization.utils.color_schemes import ColorScheme
|
||||
from tests.visualization._plotly_doubles import plotly_doubles
|
||||
|
||||
class TestVisualizationAdvanced(unittest.TestCase):
|
||||
|
||||
@@ -29,6 +16,10 @@ class TestVisualizationAdvanced(unittest.TestCase):
|
||||
self.mock_logger = MagicMock()
|
||||
self.mock_tracker = MagicMock()
|
||||
|
||||
stack = ExitStack()
|
||||
self.addCleanup(stack.close)
|
||||
stack.enter_context(plotly_doubles(analytics_visualizer, embedding_visualizer))
|
||||
|
||||
self.patchers = [
|
||||
patch('semantica.visualization.analytics_visualizer.get_logger', return_value=self.mock_logger),
|
||||
patch('semantica.visualization.analytics_visualizer.get_progress_tracker', return_value=self.mock_tracker),
|
||||
@@ -53,22 +44,17 @@ class TestVisualizationAdvanced(unittest.TestCase):
|
||||
viz = AnalyticsVisualizer()
|
||||
centrality = {"n1": 0.5, "n2": 0.3}
|
||||
|
||||
# Access the mock that was injected
|
||||
import plotly.graph_objects as go
|
||||
# Reset mock to ensure clean state
|
||||
go.Bar.reset_mock()
|
||||
|
||||
viz.visualize_centrality_rankings(centrality, output="interactive")
|
||||
go.Bar.assert_called()
|
||||
with (
|
||||
patch('semantica.visualization.analytics_visualizer.go.Bar') as mock_bar,
|
||||
patch('semantica.visualization.analytics_visualizer.go.Figure'),
|
||||
):
|
||||
viz.visualize_centrality_rankings(centrality, output="interactive")
|
||||
mock_bar.assert_called()
|
||||
|
||||
def test_visualize_community_structure(self):
|
||||
viz = AnalyticsVisualizer()
|
||||
|
||||
if hasattr(viz, 'visualize_community_structure'):
|
||||
import plotly.graph_objects as go
|
||||
# Reset mocks
|
||||
go.Figure.reset_mock()
|
||||
|
||||
graph = MagicMock()
|
||||
communities = {"c1": ["n1", "n2"]}
|
||||
|
||||
@@ -89,8 +75,6 @@ class TestVisualizationAdvanced(unittest.TestCase):
|
||||
viz = EmbeddingVisualizer()
|
||||
embeddings = np.random.rand(10, 128)
|
||||
|
||||
import plotly.graph_objects as go
|
||||
|
||||
# Mock UMAP/TSNE/PCA
|
||||
with patch('semantica.visualization.embedding_visualizer.umap') as mock_umap, \
|
||||
patch('semantica.visualization.embedding_visualizer.TSNE') as mock_tsne, \
|
||||
@@ -116,12 +100,13 @@ class TestVisualizationAdvanced(unittest.TestCase):
|
||||
viz = EmbeddingVisualizer()
|
||||
embeddings = np.random.rand(5, 5)
|
||||
|
||||
import plotly.graph_objects as go
|
||||
go.Heatmap.reset_mock()
|
||||
|
||||
if hasattr(viz, 'visualize_similarity_heatmap'):
|
||||
viz.visualize_similarity_heatmap(embeddings)
|
||||
go.Heatmap.assert_called()
|
||||
with (
|
||||
patch('semantica.visualization.embedding_visualizer.go.Heatmap') as mock_heatmap,
|
||||
patch('semantica.visualization.embedding_visualizer.go.Figure'),
|
||||
):
|
||||
viz.visualize_similarity_heatmap(embeddings)
|
||||
mock_heatmap.assert_called()
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -1,26 +1,9 @@
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, patch
|
||||
import sys
|
||||
import numpy as np
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
# Mock heavy libraries before importing visualization modules
|
||||
sys.modules['matplotlib'] = MagicMock()
|
||||
sys.modules['matplotlib.pyplot'] = MagicMock()
|
||||
sys.modules['matplotlib.colors'] = MagicMock()
|
||||
sys.modules['matplotlib.patches'] = MagicMock()
|
||||
sys.modules['plotly'] = MagicMock()
|
||||
sys.modules['plotly.express'] = MagicMock()
|
||||
sys.modules['plotly.graph_objects'] = MagicMock()
|
||||
sys.modules['plotly.subplots'] = MagicMock()
|
||||
sys.modules['seaborn'] = MagicMock()
|
||||
sys.modules['umap'] = MagicMock()
|
||||
sys.modules['sklearn'] = MagicMock()
|
||||
sys.modules['sklearn.decomposition'] = MagicMock()
|
||||
sys.modules['sklearn.manifold'] = MagicMock()
|
||||
sys.modules['networkx'] = MagicMock()
|
||||
sys.modules['graphviz'] = MagicMock()
|
||||
|
||||
# Import visualizers
|
||||
from semantica.visualization.kg_visualizer import KGVisualizer
|
||||
@@ -52,24 +35,24 @@ class TestVisualizationComprehensive(unittest.TestCase):
|
||||
patch('semantica.visualization.analytics_visualizer.get_progress_tracker', return_value=self.mock_tracker),
|
||||
patch('semantica.visualization.temporal_visualizer.get_logger', return_value=self.mock_logger),
|
||||
patch('semantica.visualization.temporal_visualizer.get_progress_tracker', return_value=self.mock_tracker),
|
||||
# Mock Layouts
|
||||
patch('semantica.visualization.kg_visualizer.ForceDirectedLayout', MagicMock()),
|
||||
patch('semantica.visualization.kg_visualizer.HierarchicalLayout', MagicMock()),
|
||||
patch('semantica.visualization.kg_visualizer.CircularLayout', MagicMock()),
|
||||
patch('semantica.visualization.ontology_visualizer.HierarchicalLayout', MagicMock()),
|
||||
patch('semantica.visualization.semantic_network_visualizer.ForceDirectedLayout', MagicMock()),
|
||||
patch('semantica.visualization.kg_visualizer.go', MagicMock()),
|
||||
patch('semantica.visualization.kg_visualizer.px', MagicMock()),
|
||||
patch('semantica.visualization.ontology_visualizer.go', MagicMock()),
|
||||
patch('semantica.visualization.ontology_visualizer.make_subplots', MagicMock()),
|
||||
patch('semantica.visualization.embedding_visualizer.go', MagicMock()),
|
||||
patch('semantica.visualization.embedding_visualizer.px', MagicMock()),
|
||||
patch('semantica.visualization.semantic_network_visualizer.go', MagicMock()),
|
||||
patch('semantica.visualization.semantic_network_visualizer.px', MagicMock()),
|
||||
patch('semantica.visualization.analytics_visualizer.go', MagicMock()),
|
||||
patch('semantica.visualization.analytics_visualizer.px', MagicMock()),
|
||||
patch('semantica.visualization.analytics_visualizer.make_subplots', MagicMock()),
|
||||
patch('semantica.visualization.temporal_visualizer.go', MagicMock()),
|
||||
patch('semantica.visualization.temporal_visualizer.px', MagicMock()),
|
||||
]
|
||||
|
||||
for p in self.patchers:
|
||||
p.start()
|
||||
|
||||
# Reset plotly mocks
|
||||
import plotly.graph_objects as go
|
||||
import plotly.express as px
|
||||
go.Figure.reset_mock()
|
||||
px.bar.reset_mock()
|
||||
px.scatter.reset_mock()
|
||||
|
||||
def tearDown(self):
|
||||
for p in self.patchers:
|
||||
p.stop()
|
||||
@@ -210,8 +193,8 @@ class TestVisualizationComprehensive(unittest.TestCase):
|
||||
embeddings = np.random.rand(10, 10)
|
||||
|
||||
# Test visualize_2d_projection (mock UMAP/PCA)
|
||||
with patch('semantica.visualization.embedding_visualizer.umap.UMAP') as MockUMAP:
|
||||
MockUMAP.return_value.fit_transform.return_value = np.random.rand(10, 2)
|
||||
with patch('semantica.visualization.embedding_visualizer.umap', MagicMock()) as mock_umap:
|
||||
mock_umap.UMAP.return_value.fit_transform.return_value = np.random.rand(10, 2)
|
||||
viz.visualize_2d_projection(embeddings)
|
||||
|
||||
# Test visualize_similarity_heatmap
|
||||
@@ -219,8 +202,8 @@ class TestVisualizationComprehensive(unittest.TestCase):
|
||||
|
||||
# Test visualize_clustering
|
||||
clusters = [0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
|
||||
with patch('semantica.visualization.embedding_visualizer.umap.UMAP') as MockUMAP:
|
||||
MockUMAP.return_value.fit_transform.return_value = np.random.rand(10, 2)
|
||||
with patch('semantica.visualization.embedding_visualizer.umap', MagicMock()) as mock_umap:
|
||||
mock_umap.UMAP.return_value.fit_transform.return_value = np.random.rand(10, 2)
|
||||
viz.visualize_clustering(embeddings, clusters)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
Reference in New Issue
Block a user