diff --git a/CHANGELOG.md b/CHANGELOG.md
index 13a505e..cac8152 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 <!-- next-header -->
 ## [Unreleased] - ReleaseDate
 
+### Added
+
+- Add `str::contains_all` function
+
 ## [3.0.3] - 2023-04-13
 
 ### Internal
diff --git a/src/lib.rs b/src/lib.rs
index ea0214c..5f87523 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -143,6 +143,7 @@
 //! - [`predicate::str::ends_with`]: Specified string must end with the given needle.
 //! - [`predicate::str::contains`]: Specified string must contain the given needle.
 //!   - [`predicate::str::contains(...).count`]: Required number of times the needle must show up.
+//! - [`predicate::str::contains_all`]: Specified string must contain all given needles.
 //! - [`predicate::str::is_match`]: Specified string must match the given regex.
 //!   - [`predicate::str::is_match(...).count`]: Required number of times the match must show up.
 //! - [`str_pred.trim`]: Trim whitespace before passing it to `str_pred`.
@@ -188,6 +189,7 @@
 //! [`predicate::path::missing`]: prelude::predicate::path::missing()
 //! [`predicate::str::contains(...).count`]: str::ContainsPredicate::count()
 //! [`predicate::str::contains`]: prelude::predicate::str::contains()
+//! [`predicate::str::contains_all`]: prelude::predicate::str::contains_all()
 //! [`predicate::str::diff`]: prelude::predicate::str::diff()
 //! [`predicate::str::ends_with`]: prelude::predicate::str::ends_with()
 //! [`predicate::str::is_empty`]: prelude::predicate::str::is_empty()
diff --git a/src/prelude.rs b/src/prelude.rs
index 8cfd447..f9413db 100644
--- a/src/prelude.rs
+++ b/src/prelude.rs
@@ -28,7 +28,7 @@ pub mod predicate {
     /// This module contains predicates specific to string handling.
     pub mod str {
         pub use crate::str::is_empty;
-        pub use crate::str::{contains, ends_with, starts_with};
+        pub use crate::str::{contains, contains_all, ends_with, starts_with};
 
         #[cfg(feature = "diff")]
         pub use crate::str::diff;
diff --git a/src/str/basics.rs b/src/str/basics.rs
index 4128a8e..494b34f 100644
--- a/src/str/basics.rs
+++ b/src/str/basics.rs
@@ -288,3 +288,81 @@ where
         pattern: pattern.into(),
     }
 }
+
+/// Predicate that checks for all patterns.
+///
+/// This is created by `predicates::str:contains_all`.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct ContainsAllPredicate {
+    patterns: Vec<String>,
+}
+
+impl Predicate<str> for ContainsAllPredicate {
+    fn eval(&self, variable: &str) -> bool {
+        for pattern in &self.patterns {
+            if !variable.contains(pattern) {
+                return false;
+            }
+        }
+
+        true
+    }
+
+    fn find_case<'a>(&'a self, expected: bool, variable: &str) -> Option<reflection::Case<'a>> {
+        let mut missing = None;
+
+        for pattern in &self.patterns {
+            if !variable.contains(pattern) && !expected {
+                missing = Some(pattern)
+            }
+        }
+
+        match missing {
+            Some(m) => Some(
+                reflection::Case::new(Some(self), false)
+                    .add_product(reflection::Product::new("missing", m.to_owned())),
+            ),
+            None => None,
+        }
+    }
+}
+
+impl reflection::PredicateReflection for ContainsAllPredicate {}
+
+impl fmt::Display for ContainsAllPredicate {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let palette = crate::Palette::new(f.alternate());
+        write!(
+            f,
+            "{}.{}({})",
+            palette.var("var"),
+            palette.description("contains_all"),
+            palette.expected(format!("{:?}", &self.patterns)),
+        )
+    }
+}
+
+/// Creates a new `Predicate` that ensures a str contains `pattern`
+///
+/// # Examples
+///
+/// ```
+/// use predicates::prelude::*;
+///
+/// let predicate_fn = predicate::str::contains_all(vec!["One", "Two", "Three"]);
+/// assert_eq!(true, predicate_fn.eval("One Two Three"));
+/// assert_eq!(false, predicate_fn.eval("One Two Four"));
+/// assert_eq!(false, predicate_fn.eval("Four Five Six"));
+/// ```
+pub fn contains_all<P, T>(patterns: P) -> ContainsAllPredicate
+where
+    P: IntoIterator<Item = T>,
+    T: AsRef<str>,
+{
+    let patterns: Vec<_> = patterns
+        .into_iter()
+        .map(|p| p.as_ref().to_string())
+        .collect();
+
+    ContainsAllPredicate { patterns }
+}