objective-c - 如何在 objective-c 中比较两个具有很多属性的对象

标签 objective-c unit-testing serialization deserialization xctest

我想测试类型为 Basket 的对象篮子的反序列化,其中包含类型为 AProduct 的对象数组。

我所有的产品类都继承自抽象类 AProduct

渐渐地,我会有越来越多的产品类别,每个类别都有不同的属性。

目前,这就是我测试篮子反序列化的方式:

Basket *oldBasket = BasketInstance;

[BasketInstance resetBasket]; // BasketInstance is a macro which points on the current Basket in singleton.

[BasketInstance deserializeItself:[self getBasketSerializedForTest]]; // getBasketSerializedForTest return a dictionnary of the same basket serialized.

XCTAssertTrue([BasketInstance isEqual:oldBasket], @"Basket deserialization is no correct");

此实现的主要问题是我必须重写 Basket 类中的 isEqual 方法,并且对于每个 AProduct 对象,重写 isEqual 方法来检查所有的属性。我认为这是非常危险的,因为如果我或其他开发人员添加了一个新属性并忘记在 isEqual 方法上检查该属性的值,则篮子的单元测试可能会成功,但反序列化失败.

是否有任何解决方案可以避免这种风险?虽然我解析了类的所有属性并为每个属性运行 isEqual 方法,但我希望有更好的解决方案。

谢谢

最佳答案

标准的 OOP 方法是每个子类测试它自己的属性。所以每个属性都会被检查一次。我认为这很简单,也很容易。完全没有危险,而且比处理运行时更简单。

@interface AProduct : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *stockIdentifier;
@end

@implementation AProduct
-(BOOL)isEqual:(id)object
{
    BOOL equal= [object isKindOfClass:[self class]]
                && [[(AProduct *)object name] isEqualToString:self.name]
                && [[(AProduct *)object stockIdentifier] isEqualToString:self.stockIdentifier];
    return equal;
}
@end


@interface Shirt : AProduct
@property (nonatomic, copy) NSString *size;
@property (nonatomic, copy) NSString *color;
@end


@implementation Shirt
-(BOOL)isEqual:(id)object
{
    BOOL equal = [super isEqual:object];
    if (equal) {
        equal = [[(Shirt *)object size] isEqualToString:self.size]
                && [[(Shirt *)object color] isEqualToString:self.color];
    }

    return equal;
}

并且由于您担心单元测试:您应该编写一个我认为也会失败的相等性测试。如果红色和蓝色衬衫确实评估为相等,则此测试失败并且您知道您必须查看 -isEqual:


假设我只是将 AProduct 子类化为 Shirt。 AProduct 有它的 -isEqual: 工作,但我还没有在 Shirt 中覆盖和扩展它。

现在我编写如下测试方法:

- (void)testShirtInEquality
{
    Shirt *redShirt = [[Shirt alloc] init];
    redShirt.name =@"Shirt";
    redShirt.stockIdentifier =@"shirt";
    redShirt.size = @"XXL";
    redShirt.color = @"red";

    Shirt *blueShirt = [[Shirt alloc] init];
    blueShirt.name =@"Shirt";
    blueShirt.stockIdentifier =@"shirt";
    blueShirt.size = @"XXL";
    blueShirt.color = @"blue";
    XCTAssertNotEqualObjects(redShirt, blueShirt, @"not equal!");
}

它将失败,因为 -isEqual: 的 AProduct 实现将触发并发现一切都相同。现在我知道我必须编写代码来使这个测试通过,并且我添加了

-(BOOL)isEqual:(id)object
{
    BOOL equal = [super isEqual:object];
    if (equal) {
        equal = [[(Shirt *)object size] isEqualToString:self.size]
        && [[(Shirt *)object color] isEqualToString:self.color];
    }

    return equal;
}

测试通过。

所以实际上答案不是写一个动态的-isEqual:方法,而是紧紧地坚持测试驱动开发。


简单回顾一下:

TDD华尔兹

  • 写一个失败的测试
  • 编写最简单的代码来通过测试
  • 重构生产和测试代码
  • 并重复

关于objective-c - 如何在 objective-c 中比较两个具有很多属性的对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22858350/

相关文章:

c# - 使用 ISerializable 反序列化 .NET 字典

ruby - Ruby 中的 Avro 序列化 - 如何写入字符串/缓冲区而不是文件

java - 为什么 Date、BigInteger、Double 等值类应该实现 Serialized?

objective-c - 顶部带有 uitextfield 的 UIkeyboard 弹出窗口(包括图片)

objective-c - 为什么不安全地假设Objective C for循环的行为类似于C for循环?

ios - iOS 11 中的 UITableViewCell 转换中断

xcode - 如何模拟多线程环境进行单元测试?

objective-c - 这个 Xcode 警告是什么意思? "application executable is missing required architecture. ..."

angularjs - 在服务中使用 $httpBackend 进行测试

python - 在没有 manage.py 的情况下从 Django 库运行单元测试