Index: bp-core/bp-core-classes.php
--- bp-core/bp-core-classes.php
+++ bp-core/bp-core-classes.php
@@ -48,6 +48,14 @@
 	/** Variables *************************************************************/
 
 	/**
+	 * Unaltered params as passed to the constructor
+	 *
+	 * @since BuddyPress (1.8)
+	 * @var array
+	 */
+	public $query_vars_raw = array();
+
+	/**
 	 * Array of variables to query with
 	 *
 	 * @since BuddyPress (1.7)
@@ -119,8 +127,15 @@
 	 * @param string|array $query The query variables
 	 */
 	public function __construct( $query = null ) {
-		if ( ! empty( $query ) ) {
-			$this->query_vars = wp_parse_args( $query, array(
+
+		// Store the raw query vars for later access
+		$this->query_vars_raw = $query;
+
+		// Allow extending classes to register action/filter hooks
+		$this->setup_hooks();
+
+		if ( ! empty( $this->query_vars_raw ) ) {
+			$this->query_vars = wp_parse_args( $this->query_vars_raw, array(
 				'type'            => 'newest',
 				'per_page'        => 0,
 				'page'            => 1,
@@ -162,6 +177,22 @@
 	}
 
 	/**
+	 * Allow extending classes to set up action/filter hooks
+	 *
+	 * When extending BP_User_Query, you may need to use some of its
+	 * internal hooks to modify the output. It's not convenient to call
+	 * add_action() or add_filter() in your class constructor, because
+	 * BP_User_Query::__construct() contains a fair amount of logic that
+	 * you may not want to override in your class. Define this method in
+	 * your own class if you need a place where your extending class can
+	 * add its hooks early in the query-building process. See
+	 * BP_Group_Member_Query::setup_hooks() for an example.
+	 *
+	 * @since BuddyPress (1.8)
+	 */
+	public function setup_hooks() {}
+
+	/**
 	 * Prepare the query for user_ids
 	 *
 	 * @since BuddyPress (1.7)
@@ -284,9 +315,10 @@
 		/** WHERE *************************************************************/
 
 		// 'include' - User ids to include in the results
-		if ( false !== $include ) {
-			$include        = wp_parse_id_list( $include );
-			$include_ids    = $wpdb->escape( implode( ',', (array) $include ) );
+		$include     = ! empty( $include ) ? wp_parse_id_list( $include ) : array();
+		$include_ids = $this->get_include_ids( $include );
+		if ( ! empty( $include_ids ) ) {
+			$include_ids    = implode( ',', wp_parse_id_list( $include_ids ) );
 			$sql['where'][] = "u.{$this->uid_name} IN ({$include_ids})";
 		}
 
@@ -430,6 +462,26 @@
 	}
 
 	/**
+	 * Fetches the ids of users to put in the IN clause of the main query
+	 *
+	 * By default, returns the value passed to it
+	 * ($this->query_vars['include']). Having this abstracted into a
+	 * standalone method means that extending classes can override the
+	 * logic, parsing together their own user_id limits with the 'include'
+	 * ids passed to the class constructor. See BP_Group_Member_Query for
+	 * an example.
+	 *
+	 * @since BuddyPress (1.8)
+	 * @param array Sanitized array of user ids, as passed to the 'include'
+	 *   parameter of the class constructor
+	 * @return array The list of users to which the main query should be
+	 *   limited
+	 */
+	public function get_include_ids( $include = array() ) {
+		return $include;
+	}
+
+	/**
 	 * Perform a database query to populate any extra metadata we might need.
 	 * Different components will hook into the 'bp_user_query_populate_extras'
 	 * action to loop in the things they want.
Index: bp-groups/bp-groups-classes.php
--- bp-groups/bp-groups-classes.php
+++ bp-groups/bp-groups-classes.php
@@ -947,6 +947,202 @@
 	}
 }
 
+/**
+ * Query for the members of a group
+ *
+ * @since BuddyPress (1.8)
+ */
+class BP_Group_Member_Query extends BP_User_Query {
+	/**
+	 * Set up action hooks
+	 *
+	 * @since BuddyPress (1.8)
+	 */
+	public function setup_hooks() {
+		// Take this early opportunity to set the default 'type' param
+		// to 'last_modified', which will ensure that BP_User_Query
+		// trusts our order and does not try to apply its own
+		if ( empty( $this->query_vars_raw['type'] ) ) {
+			$this->query_vars_raw['type'] = 'last_modified';
+		}
+
+		// Set up our populate_extras method
+		add_action( 'bp_user_query_populate_extras', array( $this, 'populate_group_member_extras' ), 10, 2 );
+	}
+
+	/**
+	 * Get a list of user_ids to include in the IN clause of the main query
+	 *
+	 * Overrides BP_User_Query::get_include_ids(), adding our additional
+	 * group-member logic.
+	 *
+	 * @since BuddyPress (1.8)
+	 * @param array
+	 * @return array
+	 */
+	public function get_include_ids( $include ) {
+		// The following args are specific to group member queries, and
+		// are not present in the query_vars of a normal BP_User_Query.
+		// We loop through to make sure that defaults are set (though
+		// values passed to the constructor will, as usual, override
+		// these defaults).
+		$this->query_vars = wp_parse_args( $this->query_vars, array(
+			'group_id'       => 0,
+			'group_role'     => array( 'member' ),
+			'exclude_banned' => true,
+		) );
+
+		$group_member_ids = $this->get_group_member_ids();
+
+		if ( ! empty( $include ) ) {
+			$group_member_ids = array_intersect( $include, $group_member_ids );
+		}
+
+		return $group_member_ids;
+	}
+
+	/**
+	 * Get the members of the queried group
+	 *
+	 * @since BuddyPress (1.8)
+	 * @return array $ids User IDs of relevant group member ids
+	 */
+	protected function get_group_member_ids() {
+		global $wpdb;
+
+		$bp  = buddypress();
+		$sql = array(
+			'select'  => "SELECT user_id FROM {$bp->groups->table_name_members}",
+			'where'   => array(),
+			'orderby' => '',
+			'order'   => '',
+			'limit'   => '',
+		);
+
+		/** WHERE clauses *****************************************************/
+
+		$sql['where'][] = $wpdb->prepare( "group_id = %d", $this->query_vars['group_id'] );
+
+		// Role information is stored as follows: admins have
+		// is_admin = 1, mods have is_mod = 1, and members have both
+		// set to 0.
+		$roles = !empty( $this->query_vars['group_role'] ) ? $this->query_vars['group_role'] : array();
+		if ( is_string( $roles ) ) {
+			$roles = explode( ',', $roles );
+		}
+
+		// Sanitize: Only 'admin', 'mod', and 'member' are valid
+		foreach ( $roles as $role_key => $role_value ) {
+			if ( ! in_array( $role_value, array( 'admin', 'mod', 'member' ) ) ) {
+				unset( $roles[ $role_key ] );
+			}
+		}
+
+		// Remove dupes to make the count accurate, and flip for faster
+		// isset() lookups
+		$roles = array_flip( array_unique( $roles ) );
+
+		switch ( count( $roles ) ) {
+
+			// All three roles means we don't limit results
+			case 3 :
+			default :
+				$roles_sql = '';
+				break;
+
+			case 2 :
+
+				// member + mod = no admins
+				// member + admin = no mods
+				if ( isset( $roles['member'] ) ) {
+					$roles_sql = isset( $roles['admin'] ) ? "is_mod = 0" : "is_admin = 0";
+
+				// Two non-member roles must be 'admin'
+				// and 'mod'
+				} else {
+					$roles_sql = "(is_admin = 1 OR is_mod = 1)";
+				}
+				break;
+
+			case 1 :
+
+				// member only means no admins or mods
+				if ( isset( $roles['member'] ) ) {
+					$roles_sql = "is_admin = 0 AND is_mod = 0";
+
+				// Filter by that role only
+				} else {
+					$roles_sql = isset( $roles['admin'] ) ? "is_admin = 1" : "is_mod = 1";
+				}
+				break;
+
+			// No roles means no users should be returned
+			case 0 :
+				$roles_sql = $this->no_results['where'];
+				break;
+		}
+
+		if ( ! empty( $roles_sql ) ) {
+			$sql['where'][] = $roles_sql;
+		}
+
+		if ( ! empty( $this->query_vars['exclude_banned'] ) ) {
+			$sql['where'][] = "is_banned = 0";
+		}
+
+		$sql['where'] = ! empty( $sql['where'] ) ? 'WHERE ' . implode( ' AND ', $sql['where'] ) : '';
+
+		/** ORDER BY clause ***************************************************/
+
+		// @todo For now, mimicking legacy behavior of
+		// bp_group_has_members(), which has us order by date_modified
+		// only. Should abstract it in the future
+		$sql['orderby'] = "ORDER BY date_modified";
+		$sql['order']   = "DESC";
+
+		/** LIMIT clause ******************************************************/
+
+		// Technically, this is also handled by BP_User_Query, but
+		// repeating the limit here helps to make the query more
+		// efficient, by not fetching every single matching user
+		if ( ! empty( $this->query_vars['per_page'] ) && ! empty( $this->query_vars['page'] ) ) {
+			$sql['limit'] = $wpdb->prepare( "LIMIT %d, %d", absint( ( $this->query_vars['page'] - 1 ) * $this->query_vars['per_page'] ), absint( $this->query_vars['per_page'] ) );
+		}
+
+		$ids = $wpdb->get_col( "{$sql['select']} {$sql['where']} {$sql['orderby']} {$sql['order']} {$sql['limit']}" );
+
+		return $ids;
+	}
+
+	/**
+	 * Fetch additional data required in bp_group_has_members() loops
+	 *
+	 * @since BuddyPress (1.8)
+	 * @param object $query BP_User_Query object. Because we're filtering
+	 *   the current object, we use $this inside of the method instead
+	 * @param string $user_ids_sql Sanitized, comma-separated string of
+	 *   the user ids returned by the main query
+	 */
+	public function populate_group_member_extras( $query, $user_ids_sql ) {
+		global $wpdb;
+
+		$bp     = buddypress();
+		$extras = $wpdb->get_results( $wpdb->prepare( "SELECT user_id, date_modified, is_banned FROM {$bp->groups->table_name_members} WHERE user_id IN ({$user_ids_sql}) AND group_id = %d", $this->query_vars['group_id'] ) );
+
+		foreach ( (array) $extras as $extra ) {
+			if ( isset( $this->results[ $extra->user_id ] ) ) {
+				// user_id is provided for backward compatibility
+				$this->results[ $extra->user_id ]->user_id       = (int) $extra->user_id;
+				$this->results[ $extra->user_id ]->is_banned     = (int) $extra->is_banned;
+				$this->results[ $extra->user_id ]->date_modified = $extra->date_modified;
+			}
+		}
+
+		// Don't filter other BP_User_Query objects on the same page
+		remove_action( 'bp_user_query_populate_extras', array( $this, 'populate_group_member_extras' ), 10, 2 );
+	}
+}
+
 class BP_Groups_Member {
 	var $id;
 	var $group_id;
@@ -1399,6 +1595,8 @@
 	function get_all_for_group( $group_id, $limit = false, $page = false, $exclude_admins_mods = true, $exclude_banned = true, $exclude = false ) {
 		global $bp, $wpdb;
 
+		_deprecated_function( __METHOD__, '1.8', 'BP_Group_Member_Query' );
+
 		$pag_sql = '';
 		if ( !empty( $limit ) && !empty( $page ) )
 			$pag_sql = $wpdb->prepare( "LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
Index: bp-groups/bp-groups-functions.php
--- bp-groups/bp-groups-functions.php
+++ bp-groups/bp-groups-functions.php
@@ -382,8 +382,12 @@
 /**
  * Fetch the members of a group
  *
- * Procedural wrapper for BP_Groups_Member::get_all_for_group().
+ * Since BuddyPress 1.8, a procedural wrapper for BP_Group_Member_Query.
+ * Previously called BP_Groups_Member::get_all_for_group().
  *
+ * To use the legacy query, filter 'bp_use_legacy_group_member_query',
+ * returning true.
+ *
  * @param int $group_id
  * @param int $limit Maximum members to return
  * @param int $page The page of results to return (requires $limit)
@@ -392,8 +396,38 @@
  * @param array|string $exclude Array or comma-sep list of users to exclude
  * @return array Multi-d array of 'members' list and 'count'
  */
-function groups_get_group_members( $group_id, $limit = false, $page = false, $exclude_admins_mods = true, $exclude_banned = true, $exclude = false ) {
-	return BP_Groups_Member::get_all_for_group( $group_id, $limit, $page, $exclude_admins_mods, $exclude_banned, $exclude );
+function groups_get_group_members( $group_id, $limit = false, $page = false, $exclude_admins_mods = true, $exclude_banned = true, $exclude = false, $group_role = false ) {
+
+	// For legacy users. Use of BP_Groups_Member::get_all_for_group()
+	// is deprecated.
+	if ( apply_filters( 'bp_use_legacy_group_member_query', false, __FUNCTION__, func_get_args() ) ) {
+		$retval = BP_Groups_Member::get_all_for_group( $group_id, $limit, $page, $exclude_admins_mods, $exclude_banned, $exclude );
+	} else {
+
+		// exclude_admins_mods is a legacy argument. Convert to group_role
+		if ( empty( $group_role ) ) {
+			$group_role = $exclude_admins_mods ? array( 'member' ) : array( 'member', 'mod', 'admin' );
+		}
+
+		// Perform the group member query (extends BP_User_Query)
+		$members = new BP_Group_Member_Query( array(
+			'group_id'       => $group_id,
+			'per_page'       => $limit,
+			'page'           => $page,
+			'group_role'     => $group_role,
+			'exclude_banned' => $exclude_banned,
+			'exclude'        => $exclude,
+			'type'           => 'last_modified',
+		) );
+
+		// Structure the return value as expected by the template functions
+		$retval = array(
+			'members' => array_values( $members->results ),
+			'count'   => $members->total_users
+		);
+	}
+
+	return $retval;
 }
 
 function groups_get_total_member_count( $group_id ) {
Index: bp-groups/bp-groups-template.php
--- bp-groups/bp-groups-template.php
+++ bp-groups/bp-groups-template.php
@@ -1897,11 +1897,11 @@
 	var $pag_links;
 	var $total_group_count;
 
-	function __construct( $group_id, $per_page, $max, $exclude_admins_mods, $exclude_banned, $exclude ) {
+	function __construct( $group_id, $per_page, $max, $exclude_admins_mods, $exclude_banned, $exclude, $group_role = false ) {
 
 		$this->pag_page = isset( $_REQUEST['mlpage'] ) ? intval( $_REQUEST['mlpage'] ) : 1;
 		$this->pag_num  = isset( $_REQUEST['num'] ) ? intval( $_REQUEST['num'] ) : $per_page;
-		$this->members  = BP_Groups_Member::get_all_for_group( $group_id, $this->pag_num, $this->pag_page, $exclude_admins_mods, $exclude_banned, $exclude );
+		$this->members  = groups_get_group_members( $group_id, $this->pag_num, $this->pag_page, $exclude_admins_mods, $exclude_banned, $exclude, $group_role );
 
 		if ( !$max || $max >= (int) $this->members['count'] )
 			$this->total_member_count = (int) $this->members['count'];
@@ -1983,10 +1983,11 @@
 		'max' => false,
 		'exclude' => false,
 		'exclude_admins_mods' => 1,
-		'exclude_banned' => 1
+		'exclude_banned' => 1,
+		'group_role' => false,
 	) );
 
-	$members_template = new BP_Groups_Group_Members_Template( $r['group_id'], $r['per_page'], $r['max'], (int) $r['exclude_admins_mods'], (int) $r['exclude_banned'], $r['exclude'] );
+	$members_template = new BP_Groups_Group_Members_Template( $r['group_id'], $r['per_page'], $r['max'], (int) $r['exclude_admins_mods'], (int) $r['exclude_banned'], $r['exclude'], $r['group_role'] );
 	return apply_filters( 'bp_group_has_members', $members_template->has_members(), $members_template );
 }
 
Index: tests/includes/testcase.php
--- tests/includes/testcase.php
+++ tests/includes/testcase.php
@@ -223,14 +223,18 @@
 		return $user_id;
 	}
 
-	public static function add_user_to_group( $user_id, $group_id ) {
+	public static function add_user_to_group( $user_id, $group_id, $args = array() ) {
+		$r = wp_parse_args( $args, array(
+			'date_modified' => bp_core_current_time(),
+		) );
+
 		$new_member                = new BP_Groups_Member;
 		$new_member->group_id      = $group_id;
 		$new_member->user_id       = $user_id;
 		$new_member->inviter_id    = 0;
 		$new_member->is_admin      = 0;
 		$new_member->user_title    = '';
-		$new_member->date_modified = bp_core_current_time();
+		$new_member->date_modified = $r['date_modified'];
 		$new_member->is_confirmed  = 1;
 
 		$new_member->save();
Index: tests/testcases/groups/class-bp-group-member-query.php
--- tests/testcases/groups/class-bp-group-member-query.php No Base Revision
+++ tests/testcases/groups/class-bp-group-member-query.php Locally New
@@ -0,0 +1,226 @@
+<?php
+/**
+ * @group groups
+ * @group BP_Group_Member_Query
+ */
+class BP_Tests_BP_Group_Member_Query_TestCases extends BP_UnitTestCase {
+	/**
+	 * Make sure that a manual 'include' param is parsed correctly with
+	 * BP_Group_Member_Query's limiting of the query to group members
+	 */
+	public function test_with_include() {
+		$g = $this->factory->group->create();
+		$u1 = $this->create_user();
+		$u2 = $this->create_user();
+		$u3 = $this->create_user();
+
+		$this->add_user_to_group( $u1, $g, array( 'date_modified' => gmdate( 'Y-m-d H:i:s', $time - 100 ) ) );
+		$this->add_user_to_group( $u2, $g, array( 'date_modified' => gmdate( 'Y-m-d H:i:s', $time - 200 ) ) );
+
+		$query = new BP_Group_Member_Query( array(
+			'group_id' => $g,
+			'include' => array( $u2 ),
+		) );
+
+		$ids = wp_parse_id_list( array_keys( $query->results ) );
+		$this->assertEquals( array( $u2, ), $ids );
+	}
+
+	// Make sure we're falling back on 'member'
+	public function test_with_group_role_null() {
+		$g = $this->factory->group->create();
+		$u1 = $this->create_user();
+		$u2 = $this->create_user();
+		$u3 = $this->create_user();
+
+		$this->add_user_to_group( $u1, $g, array( 'date_modified' => gmdate( 'Y-m-d H:i:s', $time - 100 ) ) );
+		$this->add_user_to_group( $u2, $g, array( 'date_modified' => gmdate( 'Y-m-d H:i:s', $time - 200 ) ) );
+		$this->add_user_to_group( $u3, $g, array( 'date_modified' => gmdate( 'Y-m-d H:i:s', $time - 300 ) ) );
+
+		$m1 = new BP_Groups_Member( $u1, $g );
+		$m1->promote( 'admin' );
+		$m2 = new BP_Groups_Member( $u2, $g );
+		$m2->promote( 'mod' );
+
+		$query = new BP_Group_Member_Query( array(
+			'group_id' => $g,
+		) );
+
+		$expected = new BP_Group_Member_Query( array(
+			'group_id' => $g,
+			'group_role' => array( 'member' ),
+		) );
+
+		$this->assertEquals( $expected->results, $query->results );
+	}
+
+	public function test_with_group_role_member() {
+		$g = $this->factory->group->create();
+		$u1 = $this->create_user();
+		$u2 = $this->create_user();
+		$u3 = $this->create_user();
+
+		$this->add_user_to_group( $u1, $g, array( 'date_modified' => gmdate( 'Y-m-d H:i:s', $time - 100 ) ) );
+		$this->add_user_to_group( $u2, $g, array( 'date_modified' => gmdate( 'Y-m-d H:i:s', $time - 200 ) ) );
+		$this->add_user_to_group( $u3, $g, array( 'date_modified' => gmdate( 'Y-m-d H:i:s', $time - 300 ) ) );
+
+		$m1 = new BP_Groups_Member( $u1, $g );
+		$m1->promote( 'admin' );
+		$m2 = new BP_Groups_Member( $u2, $g );
+		$m2->promote( 'mod' );
+
+		$query_members = new BP_Group_Member_Query( array(
+			'group_id' => $g,
+			'group_role' => array( 'member' ),
+		) );
+
+		$ids = wp_parse_id_list( array_keys( $query_members->results ) );
+		$this->assertEquals( array( $u3, ), $ids );
+	}
+
+	public function test_with_group_role_mod() {
+		$g = $this->factory->group->create();
+		$u1 = $this->create_user();
+		$u2 = $this->create_user();
+		$u3 = $this->create_user();
+
+		$this->add_user_to_group( $u1, $g, array( 'date_modified' => gmdate( 'Y-m-d H:i:s', $time - 100 ) ) );
+		$this->add_user_to_group( $u2, $g, array( 'date_modified' => gmdate( 'Y-m-d H:i:s', $time - 200 ) ) );
+		$this->add_user_to_group( $u3, $g, array( 'date_modified' => gmdate( 'Y-m-d H:i:s', $time - 300 ) ) );
+
+		$m1 = new BP_Groups_Member( $u1, $g );
+		$m1->promote( 'admin' );
+		$m2 = new BP_Groups_Member( $u2, $g );
+		$m2->promote( 'mod' );
+
+		$query_members = new BP_Group_Member_Query( array(
+			'group_id' => $g,
+			'group_role' => array( 'mod' ),
+		) );
+
+		$ids = wp_parse_id_list( array_keys( $query_members->results ) );
+		$this->assertEquals( array( $u2, ), $ids );
+	}
+
+	public function test_with_group_role_admin() {
+		$g = $this->factory->group->create();
+		$u1 = $this->create_user();
+		$u2 = $this->create_user();
+		$u3 = $this->create_user();
+
+		$this->add_user_to_group( $u1, $g, array( 'date_modified' => gmdate( 'Y-m-d H:i:s', $time - 100 ) ) );
+		$this->add_user_to_group( $u2, $g, array( 'date_modified' => gmdate( 'Y-m-d H:i:s', $time - 200 ) ) );
+		$this->add_user_to_group( $u3, $g, array( 'date_modified' => gmdate( 'Y-m-d H:i:s', $time - 300 ) ) );
+
+		$m1 = new BP_Groups_Member( $u1, $g );
+		$m1->promote( 'admin' );
+		$m2 = new BP_Groups_Member( $u2, $g );
+		$m2->promote( 'mod' );
+
+		$query_members = new BP_Group_Member_Query( array(
+			'group_id' => $g,
+			'group_role' => array( 'admin' ),
+		) );
+
+		$ids = wp_parse_id_list( array_keys( $query_members->results ) );
+		$this->assertEquals( array( $u1, ), $ids );
+	}
+
+	public function test_with_group_role_member_mod() {
+		$g = $this->factory->group->create();
+		$u1 = $this->create_user();
+		$u2 = $this->create_user();
+		$u3 = $this->create_user();
+
+		$this->add_user_to_group( $u1, $g, array( 'date_modified' => gmdate( 'Y-m-d H:i:s', $time - 100 ) ) );
+		$this->add_user_to_group( $u2, $g, array( 'date_modified' => gmdate( 'Y-m-d H:i:s', $time - 200 ) ) );
+		$this->add_user_to_group( $u3, $g, array( 'date_modified' => gmdate( 'Y-m-d H:i:s', $time - 300 ) ) );
+
+		$m1 = new BP_Groups_Member( $u1, $g );
+		$m1->promote( 'admin' );
+		$m2 = new BP_Groups_Member( $u2, $g );
+		$m2->promote( 'mod' );
+
+		$query_members = new BP_Group_Member_Query( array(
+			'group_id' => $g,
+			'group_role' => array( 'member', 'mod' ),
+		) );
+
+		$ids = wp_parse_id_list( array_keys( $query_members->results ) );
+		$this->assertEquals( array( $u2, $u3, ), $ids );
+	}
+
+	public function test_with_group_role_member_admin() {
+		$g = $this->factory->group->create();
+		$u1 = $this->create_user();
+		$u2 = $this->create_user();
+		$u3 = $this->create_user();
+
+		$this->add_user_to_group( $u1, $g, array( 'date_modified' => gmdate( 'Y-m-d H:i:s', $time - 100 ) ) );
+		$this->add_user_to_group( $u2, $g, array( 'date_modified' => gmdate( 'Y-m-d H:i:s', $time - 200 ) ) );
+		$this->add_user_to_group( $u3, $g, array( 'date_modified' => gmdate( 'Y-m-d H:i:s', $time - 300 ) ) );
+
+		$m1 = new BP_Groups_Member( $u1, $g );
+		$m1->promote( 'admin' );
+		$m2 = new BP_Groups_Member( $u2, $g );
+		$m2->promote( 'mod' );
+
+		$query_members = new BP_Group_Member_Query( array(
+			'group_id' => $g,
+			'group_role' => array( 'member', 'admin' ),
+		) );
+
+		$ids = wp_parse_id_list( array_keys( $query_members->results ) );
+		$this->assertEquals( array( $u1, $u3, ), $ids );
+	}
+
+	public function test_with_group_role_mod_admin() {
+		$g = $this->factory->group->create();
+		$u1 = $this->create_user();
+		$u2 = $this->create_user();
+		$u3 = $this->create_user();
+
+		$this->add_user_to_group( $u1, $g, array( 'date_modified' => gmdate( 'Y-m-d H:i:s', $time - 100 ) ) );
+		$this->add_user_to_group( $u2, $g, array( 'date_modified' => gmdate( 'Y-m-d H:i:s', $time - 200 ) ) );
+		$this->add_user_to_group( $u3, $g, array( 'date_modified' => gmdate( 'Y-m-d H:i:s', $time - 300 ) ) );
+
+		$m1 = new BP_Groups_Member( $u1, $g );
+		$m1->promote( 'admin' );
+		$m2 = new BP_Groups_Member( $u2, $g );
+		$m2->promote( 'mod' );
+
+		$query_members = new BP_Group_Member_Query( array(
+			'group_id' => $g,
+			'group_role' => array( 'mod', 'admin' ),
+		) );
+
+		$ids = wp_parse_id_list( array_keys( $query_members->results ) );
+		$this->assertEquals( array( $u1, $u2, ), $ids );
+	}
+
+	public function test_with_group_role_member_mod_admin() {
+		$g = $this->factory->group->create();
+		$u1 = $this->create_user();
+		$u2 = $this->create_user();
+		$u3 = $this->create_user();
+
+		$this->add_user_to_group( $u1, $g, array( 'date_modified' => gmdate( 'Y-m-d H:i:s', $time - 100 ) ) );
+		$this->add_user_to_group( $u2, $g, array( 'date_modified' => gmdate( 'Y-m-d H:i:s', $time - 200 ) ) );
+		$this->add_user_to_group( $u3, $g, array( 'date_modified' => gmdate( 'Y-m-d H:i:s', $time - 300 ) ) );
+
+		$m1 = new BP_Groups_Member( $u1, $g );
+		$m1->promote( 'admin' );
+		$m2 = new BP_Groups_Member( $u2, $g );
+		$m2->promote( 'mod' );
+
+		$query_members = new BP_Group_Member_Query( array(
+			'group_id' => $g,
+			'group_role' => array( 'member', 'mod', 'admin' ),
+		) );
+
+		$ids = wp_parse_id_list( array_keys( $query_members->results ) );
+		$this->assertEquals( array( $u1, $u2, $u3, ), $ids );
+	}
+
+
+}
Index: tests/testcases/groups/template.php
--- tests/testcases/groups/template.php
+++ tests/testcases/groups/template.php
@@ -118,8 +118,62 @@
 	}
 
 	/**
+	 * Switching from BP_Groups_Member to BP_Group_Member_Query meant a
+	 * change in the format of the values returned from the query. For
+	 * backward compatibility, we translate some of the return values
+	 * of BP_Group_Member_Query to the older format. This test makes sure
+	 * that the translation happens properly.
+	 *
 	 * @group bp_group_has_members
 	 */
+	public function test_bp_group_has_members_backpat_retval_format() {
+		$g = $this->factory->group->create();
+		$u1 = $this->create_user();
+		$u2 = $this->create_user();
+
+		$date_modified = gmdate( 'Y-m-d H:i:s', time() - 100 );
+
+		$new_member                = new BP_Groups_Member;
+		$new_member->group_id      = $g;
+		$new_member->user_id       = $u1;
+		$new_member->inviter_id    = $u2;
+		$new_member->is_admin      = 0;
+		$new_member->user_title    = '';
+		$new_member->date_modified = $date_modified;
+		$new_member->is_confirmed  = 1;
+		$new_member->save();
+
+		global $members_template;
+		bp_group_has_members( array(
+			'group_id' => $g,
+		) );
+
+		$u1_object = new WP_User( $u1 );
+
+		$expected = new stdClass;
+		$expected->user_id = $u1;
+		$expected->date_modified = $date_modified;
+		$expected->is_banned = 0;
+		$expected->user_login = $u1_object->user_login;
+		$expected->user_nicename = $u1_object->user_nicename;
+		$expected->user_email = $u1_object->user_email;
+		$expected->display_name = $u1_object->display_name;
+
+		// In order to use assertEquals, we need to discard the
+		// irrelevant properties of the found object. Hack alert
+		$found = new stdClass;
+		foreach ( array( 'user_id', 'date_modified', 'is_banned', 'user_login', 'user_nicename', 'user_email', 'display_name' ) as $key ) {
+			if ( isset( $members_template->members[0]->{$key} ) ) {
+				$found->{$key} = $members_template->members[0]->{$key};
+			}
+		}
+
+		$this->assertEquals( $expected, $found );
+	}
+
+	/**
+	 * @group bp_group_has_members
+	 */
 	public function test_bp_group_has_members_with_per_page() {
 		$g = $this->factory->group->create();
 
